Wie filtere ich ein Objektarray basierend auf Attributen?

473

Ich habe das folgende JavaScript-Array von Immobilien-Home-Objekten:

var json = {
    'homes': [{
            "home_id": "1",
            "price": "925",
            "sqft": "1100",
            "num_of_beds": "2",
            "num_of_baths": "2.0",
        }, {
            "home_id": "2",
            "price": "1425",
            "sqft": "1900",
            "num_of_beds": "4",
            "num_of_baths": "2.5",
        },
        // ... (more homes) ...     
    ]
}

var xmlhttp = eval('(' + json + ')');
homes = xmlhttp.homes;

Ich möchte in der Lage sein, einen Filter für das Objekt auszuführen, um eine Teilmenge von "Home" -Objekten zurückzugeben.

Zum Beispiel möchte ich Filter in der Lage sein , basierend auf: price, sqft, num_of_beds, und num_of_baths.

Wie kann ich in JavaScript etwas wie den folgenden Pseudocode ausführen:

var newArray = homes.filter(
    price <= 1000 & 
    sqft >= 500 & 
    num_of_beds >=2 & 
    num_of_baths >= 2.5 );

Beachten Sie, dass die Syntax nicht genau wie oben sein muss. Dies ist nur ein Beispiel.

JGreig
quelle
7
Dies scheint fast identisch mit stackoverflow.com/questions/1694717/…
Crescent Fresh
3
var json = { ... }JSON ist eine Textnotation für den Datenaustausch. (Mehr hier.) Wenn Sie mit JavaScript-Quellcode und nicht mit einer Zeichenfolge arbeiten , haben Sie es nicht mit JSON zu tun.
TJ Crowder
1
Verwenden Sie eval nicht. Es ist im Allgemeinen eine schlechte Praxis und kann Leistungsprobleme verursachen. Wir mussten nur ein paar von denen in einem Projekt loswerden, weil der Prozessor abstürzte.
SDH

Antworten:

718

Sie können die Array.prototype.filterMethode verwenden:

var newArray = homes.filter(function (el) {
  return el.price <= 1000 &&
         el.sqft >= 500 &&
         el.num_of_beds >=2 &&
         el.num_of_baths >= 2.5;
});

Live-Beispiel:

Diese Methode ist Teil des neuen ECMAScript 5th Edition- Standards und kann in fast allen modernen Browsern verwendet werden.

Für IE können Sie aus Kompatibilitätsgründen die folgende Methode einschließen:

if (!Array.prototype.filter) {
  Array.prototype.filter = function(fun /*, thisp*/) {
    var len = this.length >>> 0;
    if (typeof fun != "function")
      throw new TypeError();

    var res = [];
    var thisp = arguments[1];
    for (var i = 0; i < len; i++) {
      if (i in this) {
        var val = this[i];
        if (fun.call(thisp, val, i, this))
          res.push(val);
      }
    }
    return res;
  };
}
CMS
quelle
8
Was ist / *, thisp * /?
JGreig
2
@JGreig: Ist nur ein Kommentar, der angibt, dass ein optionales Argument übergeben werden kann. Das Argument wird nicht direkt angegeben, da der ECMA-Standard genau besagt, dass diese Methode nur ein Argument erwarten sollte ( Array.prototype.filter.length == 1;). Wenn Sie das zweite Argument verwenden, wird es als thisWert in der Rückruffunktion verwendet.
CMS
1
@CMS, Sind Sie sicher, dass dieser Code funktioniert? Dies gibt ein leeres Array zurück, selbst wenn ich den Preis sehr hoch und die Quadratmeter / Betten / Bäder sehr niedrig eingestellt habe. Sind Sie sicher, dass dieser Code funktioniert?
JGreig
2
@JGreig: Ja, es funktioniert, Sie sollten Ihre Kriterien überprüfen. Vielleicht können Sie ein vollständigeres Beispiel Ihres JSON in pastie.org oder jsbin.com und die Kriterien, die Sie zum Filtern verwenden, veröffentlichen, damit ich Ihnen besser helfen kann.
CMS
2
@CMS Es wäre gut, wenn die Antwort die Tatsache enthält, dass ein neues Array zurückgegeben wird und das ursprüngliche Array nicht geändert wird. Obwohl dies in dem bereitgestellten Link gesagt wird, finde ich die Antwort ohne diese unvollständig oder ungenau
GWorking
29

Sie können versuchen, ein Framework wie jLinq zu verwenden. Im Folgenden finden Sie ein Codebeispiel für die Verwendung von jLinq

var results = jLinq.from(data.users)
.startsWith("first", "a")
.orEndsWith("y")
.orderBy("admin", "age")
.select();

Weitere Informationen finden Sie unter dem Link http://www.hugoware.net/projects/jlinq

Rutesh Makhijani
quelle
26

Ich bevorzuge das Underscore-Framework. Es werden viele nützliche Operationen mit Objekten vorgeschlagen. Deine Aufgabe:

var newArray = homes.filter(
    price <= 1000 & 
    sqft >= 500 &
    num_of_beds >=2 & 
    num_of_baths >= 2.5);

kann wie folgt überschrieben werden:

var newArray = _.filter (homes, function(home) {
    return home.price<=1000 && sqft>=500 && num_of_beds>=2 && num_of_baths>=2.5;
});

Hoffe, es wird nützlich für Sie sein!

JuliaCesar
quelle
Wie funktioniert dieser Unterstrich und was genau bedeutet er?
John Demetriou
4
Gerade entdeckt underscore-query( github.com/davidgtonge/underscore-query ), das MongoDB-ähnliche Syntax verwendet, um Javascript-Arrays abzufragen. Also hier würden Sie verwenden_.query(homes, {price: {$lt:1000}, sqft: {$gte: 500}, num_of_beds: {$gte:2}, num_of_baths: {$gte: 2.5}}
Prototyp
12

Ich bin überrascht, dass niemand die einzeilige Antwort gepostet hat:

const filteredHomes = json.homes.filter(x => x.price <= 1000 && x.sqft >= 500 && x.num_of_beds >=2 && x.num_of_baths >= 2.5);

... und damit Sie es leichter lesen können:

const filteredHomes = json.homes.filter( x => 
  x.price <= 1000 && 
  x.sqft >= 500 && 
  x.num_of_beds >=2 && 
  x.num_of_baths >= 2.5
);
Lou Bagel
quelle
2
wahrscheinlich, weil es die gleiche ist wie die Antwort von CMS, nur mit Pfeilfunktionen
Erich
11

Hier ist die funktionierende Geige, die in IE8 mit der Funktion jquery MAP einwandfrei funktioniert

http://jsfiddle.net/533135/Cj4j7/

json.HOMES = $.map(json.HOMES, function(val, key) {
    if (Number(val.price) <= 1000
            && Number(val.sqft) >= 500
            && Number(val.num_of_beds) >=2
            && Number(val.num_of_baths ) >= 2.5)
        return val;
});
Chetan Sandeep Renu
quelle
7

Sie können jQuery.grep () seit jQuery 1.0 verwenden:

$.grep(homes, function (h) {
  return h.price <= 1000
    && h.sqft >= 500
    && h.num_of_beds >= 2
    && h.num_of_baths >= 2.5
});
Akira Yamamoto
quelle
7

Sie könnten dies ziemlich einfach tun - es gibt wahrscheinlich viele Implementierungen, aus denen Sie auswählen können, aber dies ist meine Grundidee (und es gibt wahrscheinlich ein Format, in dem Sie mit jQuery über ein Objekt iterieren können, ich kann es mir gerade nicht vorstellen):

function filter(collection, predicate)
{
    var result = new Array();
    var length = collection.length;

    for(var j = 0; j < length; j++)
    {
        if(predicate(collection[j]) == true)
        {
             result.push(collection[j]);
        }
    }

    return result;
}

Und dann können Sie diese Funktion folgendermaßen aufrufen:

filter(json, function(element)
{
    if(element.price <= 1000 && element.sqft >= 500 && element.num_of_beds > 2 && element.num_of_baths > 2.5)
        return true;

    return false;
});

Auf diese Weise können Sie den Filter basierend auf dem von Ihnen definierten Prädikat aufrufen oder sogar mehrmals mit kleineren Filtern filtern.

Tejs
quelle
Bitte ändern Sie "int length" in "var length" im ersten Snippet
Malay Desai
4

Sie können selbst eine Filtermethode implementieren, die Ihren Anforderungen entspricht. So geht's:

function myfilter(array, test){
    var passedTest =[];
    for (var i = 0; i < array.length; i++) {
       if(test( array[i]))
          passedTest.push(array[i]);
    }

    return passedTest;
}

var passedHomes = myfilter(homes,function(currentHome){
     return ((currentHome.price <= 1000 )&& (currentHome.sqft >= 500 )&&(currentHome.num_of_beds >=2 )&&(currentHome.num_of_baths >= 2.5));
});

Ich hoffe es hilft!

user4620852
quelle
Mein Prädikatzustand ändert sich durch verschiedene Onclicks. Wie kann ich die aktuellen Prädikatwerte speichern und auf die Funktion myfilter anwenden, wann immer ich möchte?
Malay Desai
3

Sie sollten sich OGX.List ansehen, das Filtermethoden integriert und das Standard-Javascript-Array erweitert (sowie Gruppieren, Sortieren und Suchen). Hier ist eine Liste der Operatoren, die für die Filter unterstützt werden:

'eq' //Equal to
'eqjson' //For deep objects, JSON comparison, equal to
'neq' //Not equal to
'in' //Contains
'nin' //Doesn't contain
'lt' //Lesser than
'lte' //Lesser or equal to
'gt' //Greater than
'gte' //Greater or equal to
'btw' //Between, expects value to be array [_from_, _to_]
'substr' //Substring mode, equal to, expects value to be array [_from_, _to_, _niddle_]
'regex' //Regex match

Sie können es auf diese Weise verwenden

  let list = new OGX.List(your_array);
  list.addFilter('price', 'btw', 100, 500);
  list.addFilter('sqft', 'gte', 500);
  let filtered_list = list.filter();

Oder auch so

  let list = new OGX.List(your_array);
  let filtered_list = list.get({price:{btw:[100,500]}, sqft:{gte:500}});

Oder als Einzeiler

   let filtered_list = new OGX.List(your_array).get({price:{btw:[100,500]}, sqft:{gte:500}});
Eric
quelle
2

Oder Sie können einfach verwenden $.each(was auch für Objekte funktioniert, nicht nur für Arrays) und ein neues Array wie folgt erstellen:

var json = {
    'homes': [{
            "home_id": "1",
            "price": "925",
            "sqft": "1100",
            "num_of_beds": "2",
            "num_of_baths": "2.0",
        }, {
            "home_id": "2",
            "price": "1425",
            "sqft": "1900",
            "num_of_beds": "4",
            "num_of_baths": "2.5",
        },
        // ... (more homes) ...     
        {
            "home_id": "3-will-be-matched",
            "price": "925",
            "sqft": "1000",
            "num_of_beds": "2",
            "num_of_baths": "2.5",
        },
    ]
}

var homes = [];
$.each(json.homes, function(){
    if (this.price <= 1000
        && this.sqft >= 500
        && this.num_of_beds >= 2
        && this.num_of_baths >= 2.5
    ) {
        homes.push(this);
    }
});
Nux
quelle
2

Ich verwende meine ruleOutFunktion zum Filtern von Objekten basierend auf bestimmten unerwünschten Eigenschaftswerten. Ich verstehe, dass Sie in Ihrem Beispiel Bedingungen anstelle von Werten verwenden möchten, aber meine Antwort gilt für den Fragentitel. Daher möchte ich meine Methode hier belassen.

function ruleOut(arr, filterObj, applyAllFilters=true) {    
    return arr.filter( row => {            
        for (var field in filterObj) {
            var val = row[field];
            if (val) {                    
                if (applyAllFilters && filterObj[field].indexOf(val) > -1) return false;                
                else if (!applyAllFilters) {                        
                    return filterObj[field].filter(function(filterValue){ 
                        return (val.indexOf(filterValue)>-1);
                    }).length == 0;                 
                }
            }
        }
        return true;
    });
}

Angenommen, Sie haben eine Liste solcher Schauspieler:

let actors = [
  {userName:"Mary", job:"star", language:"Turkish"},
  {userName:"John", job:"actor", language:"Turkish"},
  {userName:"Takis", job:"star", language:"Greek"},
  {userName:"Joe", job:"star", language:"Turkish"},
  {userName:"Bill", job:"star", language:"Turkish"}
];

und Sie möchten alle Schauspieler finden, die als Holywood-Stars eingestuft sind. Ihre Nationalität sollte nicht "Englisch", "Italienisch", "Spanisch", "Griechisch" sein, und ihr Name sollte nicht "Mary" sein. "Joe". Bizzar Beispiel, ich weiß! Mit diesen Bedingungen würden Sie auf jeden Fall das folgende Objekt erstellen:

let unwantedFieldsFilter= { 
  userName: ['Mary', 'Joe'],    
  job: ['actor'],   
  language: ['English', 'Italian', 'Spanish', 'Greek']  
};

OK, jetzt, wenn Sie ruleOut(actors, unwantedFieldsFilter)nur bekommen würden

[{Benutzername: "Rechnung", Job: "Stern", Sprache: "Türkisch"}]

Und Bill ist dein Mann, da sein Name nicht "Mary", "Joe" ist, seine Nationalität nicht in ["Englisch", "Italienisch", "Spanisch", "Griechisch" enthalten ist und er ein Star ist!

In meiner Methode gibt es eine Option, die applyAllFiltersstandardmäßig wahr ist. Wenn Sie versuchen würden, ruleOut mit diesem Parametersatz als false festzulegen, funktioniert dies als ODER-Filterung anstelle von UND. Beispiel: ruleOut(actors, {job:["actor"], language:["Italian"]}, false)Würde Ihnen jeder bringen, der kein Schauspieler oder Italiener ist:

[{Benutzername: "Mary", Job: "Stern", Sprache: "Türkisch"},
{Benutzername: "Takis", Job: "Stern", Sprache: "Griechisch"},
{Benutzername: "Joe", Job: "Stern", Sprache: "Türkisch"},
{Benutzername: "Rechnung", Job: "Stern", Sprache: "Türkisch"}]

JohnPan
quelle
1
var filterHome = homes.filter(home =>
  return (home.price <= 999 &&
         home.num_of_baths >= 2.5 &&
         home.num_of_beds >=2 &&
         home.sqft >= 998));
console.log(filterHome);

Sie können diese Lambda-Funktion verwenden. Weitere Details finden Sie hier, da wir die Daten basierend auf Ihrer Bedingung filtern, die true oder false zurückgibt, und die Daten in verschiedenen Arrays sammeln, sodass Ihr tatsächliches Array nicht geändert wird.

@JGreig Bitte schauen Sie hinein.

Hardik Beladiya
quelle
1
const x = JSON.stringify(data);
const y = 'search text';

const data = x.filter(res => {
        return(JSON.stringify(res).toLocaleLowerCase()).match(x.toLocaleLowerCase());
      });
Thức Trần
quelle
0
const state.contactList = [{
    name: 'jane',
    email: '[email protected]'
  },{},{},...]

const fileredArray = state.contactsList.filter((contactItem) => {
  const regex = new RegExp(`${action.payload}`, 'gi');
  return contactItem.nameProperty.match(regex) || 
    contactItem.emailProperty.match(regex);
});


// contactList: all the contacts stored in state
// action.payload: whatever typed in search field
anoNewb
quelle