Come filtrare l'array di oggetti in base agli attributi?


473

Ho il seguente array JavaScript di oggetti domestici immobiliari:

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;

Quello che vorrei fare è essere in grado di eseguire un filtro sull'oggetto per restituire un sottoinsieme di oggetti "home".

Per esempio, io voglio essere in grado di filtrare in base a: price, sqft, num_of_beds, e num_of_baths.

Come posso eseguire qualcosa in JavaScript come lo pseudo-codice qui sotto:

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

Nota, la sintassi non deve essere esattamente come sopra. Questo è solo un esempio.


7
Questo sembra quasi identico a stackoverflow.com/questions/1694717/…
Crescent Fresh

3
var json = { ... }JSON è una notazione testuale per lo scambio di dati. (Altro qui.) Se hai a che fare con il codice sorgente JavaScript e non hai a che fare con una stringa , non hai a che fare con JSON.
TJ Crowder,

1
Non usare eval. In genere è una cattiva pratica e può causare problemi di prestazioni. Abbiamo dovuto sbarazzarci di un sacco di quelli su un progetto perché il processore si stava bloccando.
SDH,

Risposte:


719

Puoi usare il Array.prototype.filtermetodo:

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

Esempio live:

Questo metodo fa parte del nuovo standard ECMAScript 5th Edition e può essere trovato su quasi tutti i browser moderni.

Per IE, è possibile includere il seguente metodo per la compatibilità:

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;
  };
}

8
che cos'è / *, thisp * /?
JGreig,

2
@JGreig: è solo un commento per indicare che può essere passato un argomento facoltativo, l'argomento non è specificato direttamente perché lo standard ECMA afferma precisamente che questo metodo dovrebbe aspettarsi solo un argomento ( Array.prototype.filter.length == 1;). Quando si utilizza il secondo argomento, verrà utilizzato come thisvalore all'interno della funzione di callback.
CMS,

1
@CMS, sei sicuro che questo codice funzioni? Questo sta restituendo un array vuoto, anche quando ho impostato il prezzo per essere davvero alto e il sqft / letti / bagni per essere davvero basso. Sei sicuro che questo codice funzioni?
JGreig,

2
@JGreig: Sì, funziona, dovresti controllare i tuoi criteri, forse potresti pubblicare un esempio più completo del tuo JSON in pastie.org o jsbin.com e i criteri che stai usando per filtrare, così posso aiutarti meglio.
CMS,

2
@CMS Sarebbe positivo se la risposta includesse il fatto che viene restituito un nuovo array, l'array originale non viene modificato. Anche se questo è detto nel link fornito, trovo che la risposta senza questa sia incompleta o imprecisa
GWorking

29

Puoi provare a usare framework come jLinq: di seguito è riportato un esempio di codice sull'uso di jLinq

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

Per ulteriori informazioni è possibile seguire il link http://www.hugoware.net/projects/jlinq


26

Preferisco il framework Underscore. Suggerisce molte operazioni utili con gli oggetti. Il tuo compito:

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

può essere sovrascritto come:

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

Spero che ti sia utile!


Come funziona questa sottolineatura e cosa significa esattamente?
John Demetriou,

4
Appena scoperto underscore-query( github.com/davidgtonge/underscore-query ) che utilizza una sintassi simile a MongoDB per eseguire query su array javascript. Quindi qui useresti_.query(homes, {price: {$lt:1000}, sqft: {$gte: 500}, num_of_beds: {$gte:2}, num_of_baths: {$gte: 2.5}}
prototipo

12

Sono sorpreso che nessuno abbia pubblicato la risposta di una riga:

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

... e solo così puoi leggerlo più facilmente:

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

2
probabilmente perché è la stessa risposta di CMS con le funzioni freccia
Erich

11

ecco il violino funzionante che funziona perfettamente in IE8 usando la funzione MAP di jquery

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;
});

7

Puoi usare jQuery.grep () da jQuery 1.0:

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

7

Potresti farlo abbastanza facilmente - probabilmente ci sono molte implementazioni tra cui puoi scegliere, ma questa è la mia idea di base (e probabilmente c'è qualche formato in cui puoi iterare su un oggetto con jQuery, non riesco proprio a pensarci adesso):

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;
}

E quindi potresti invocare questa funzione in questo modo:

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;
});

In questo modo, puoi invocare il filtro in base a qualsiasi predicato definito o filtrare più volte utilizzando filtri più piccoli.


Modifica "int length" in "var length" nel primo snippet
Malay Desai,

4

Puoi implementare tu stesso un metodo di filtro che soddisfa le tue esigenze, ecco come:

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));
});

Spero che sia d'aiuto!


La mia condizione predicata cambia da vari onclick. Come posso memorizzare i valori correnti del predicato e applicarlo alla funzione myfilter ogni volta che voglio?
Malay Desai

3

Dovresti dare un'occhiata a OGX.List che ha incorporato i metodi di filtro ed estende l'array javascript standard (e anche raggruppamento, ordinamento e ricerca). Ecco un elenco di operatori che supporta per i filtri:

'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

Puoi usarlo in questo modo

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

O anche così

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

O come una fodera

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

2

Oppure puoi semplicemente usare $.each(che funziona anche per oggetti, non solo array) e costruire un nuovo array in questo modo:

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);
    }
});

2

Uso la mia ruleOutfunzione per filtrare gli oggetti in base a specifici valori di proprietà indesiderati. Capisco che nel tuo esempio ti piacerebbe usare le condizioni invece dei valori, ma la mia risposta è valida per il titolo della domanda, quindi vorrei lasciare il mio metodo qui.

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;
    });
}

Supponi di avere un elenco di attori come questo:

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"}
];

e vorresti trovare tutti gli attori classificati come star di Holywood, la loro nazionalità non dovrebbe essere "inglese", "italiano", "spagnolo", "greco", inoltre il loro nome non sarebbe uno di "Maria", 'Joe'. Esempio Bizzar, lo so! Ad ogni modo, con quel set di condizioni creeresti il ​​seguente oggetto:

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

OK, ora se ruleOut(actors, unwantedFieldsFilter)solo avessi

[{userName: "Bill", lavoro: "stella", lingua: "turco"}]

E Bill è il tuo uomo, dal momento che il suo nome non è uno di "Mary", "Joe", la sua nazionalità non è inclusa in ["inglese", "italiano", "spagnolo", "greco"] e in più è una stella!

C'è un'opzione nel mio metodo, che è applyAllFiltersed è vera per impostazione predefinita. Se provassi a ruleOut con questo parametro impostato su false, funzionerebbe come filtro "OR" anziché "AND". Esempio: ti ruleOut(actors, {job:["actor"], language:["Italian"]}, false)darebbe chiunque non sia un attore o un italiano:

[{userName: "Mary", lavoro: "stella", lingua: "turco"},
{userName: "Takis", lavoro: "stella", lingua: "Greco"},
{userName: "Joe", lavoro: "stella", lingua: "turco"},
{userName: "Bill", lavoro: "stella", lingua: "turco"}]


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);

Puoi usare questa funzione lambda. Maggiori dettagli possono essere trovati qui poiché stiamo filtrando i dati in base alle tue condizioni che restituiscono vero o falso e raccoglierà i dati in diversi array in modo che il tuo array reale non venga modificato.

@JGreig Per favore, guardaci dentro.


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

const data = x.filter(res => {
        return(JSON.stringify(res).toLocaleLowerCase()).match(x.toLocaleLowerCase());
      });

0
const state.contactList = [{
    name: 'jane',
    email: 'jane@gmail.com'
  },{},{},...]

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.