Jak filtrować tablicę obiektów na podstawie atrybutów?

472

Mam następującą tablicę JavaScript obiektów domowych nieruchomości:

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;

Chciałbym wykonać filtr na obiekcie, aby zwrócić podzbiór obiektów „domowych”.

Na przykład, chcę być w stanie filtra opiera się na: price, sqft, num_of_beds, inum_of_baths .

Jak mogę wykonać coś w JavaScript, np. Pseudo-kod poniżej:

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

Uwaga: składnia nie musi być dokładnie taka jak powyżej. To tylko przykład.

JGreig
źródło
7
Wydaje się to prawie identyczne jak stackoverflow.com/questions/1694717/…
Crescent Fresh
3
var json = { ... }JSON to tekstowy zapis do wymiany danych. (Więcej tutaj.) Jeśli masz do czynienia z kodem źródłowym JavaScript, a nie z łańcuchem , nie masz do czynienia z JSON.
TJ Crowder
1
Nie używaj eval. Jest to ogólnie zła praktyka i może powodować problemy z wydajnością. Musieliśmy po prostu pozbyć się wielu z nich w projekcie, ponieważ procesor się blokował.
SDH

Odpowiedzi:

718

Możesz użyć Array.prototype.filtermetody:

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

Przykład na żywo:

Ta metoda jest częścią nowego standardu ECMAScript 5. edycji i można ją znaleźć w prawie wszystkich nowoczesnych przeglądarkach.

W przypadku przeglądarki IE można dołączyć następującą metodę zgodności:

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
źródło
8
co to jest / *, thisp * /?
JGreig
2
@JGreig: To tylko komentarz wskazujący, że opcjonalny argument może zostać przekazany, argument nie jest określony bezpośrednio, ponieważ standard ECMA mówi dokładnie, że ta metoda powinna oczekiwać tylko jednego argumentu ( Array.prototype.filter.length == 1;). Gdy użyjesz drugiego argumentu, zostanie on użyty jako thiswartość wewnątrz funkcji zwrotnej.
CMS
1
@CMS, Czy na pewno ten kod działa? Zwraca to pustą tablicę, nawet gdy ustawiam cenę na naprawdę wysoką, a sqft / łóżka / wanny na naprawdę niską. Czy na pewno ten kod działa?
JGreig
2
@JGreig: Tak, to działa, powinieneś sprawdzić swoje kryteria, być może możesz opublikować pełniejszy przykład swojego JSON na pastie.org lub jsbin.com i kryteria, których używasz do filtrowania, aby pomóc ci lepiej.
CMS
2
@CMS Byłoby dobrze, gdyby odpowiedź zawierała fakt, że zwracana jest nowa tablica, pierwotna tablica nie jest modyfikowana. Mimo, że jest to powiedziane w podanym linku, odpowiedź bez tego jest niepełna lub niedokładna
GWorking
29

Możesz spróbować użyć frameworka takiego jak jLinq - poniżej znajduje się przykładowy kod użycia jLinq

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

Aby uzyskać więcej informacji, kliknij link http://www.hugoware.net/projects/jlinq

Rutesh Makhijani
źródło
26

Wolę szkielet Underscore. Sugeruje wiele przydatnych operacji na obiektach. Twoje zadanie:

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

może być przytłoczony jak:

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

Mam nadzieję, że ci się przyda!

Julia Cezar
źródło
Jak działa to podkreślenie i co to dokładnie znaczy?
John Demetriou,
4
Właśnie odkryłem underscore-query( github.com/davidgtonge/underscore-query ), który używa składni podobnej do MongoDB do zapytania o tablice javascript. Więc skorzystaj_.query(homes, {price: {$lt:1000}, sqft: {$gte: 500}, num_of_beds: {$gte:2}, num_of_baths: {$gte: 2.5}}
prototyp
12

Dziwię się, że nikt nie opublikował odpowiedzi w jednym wierszu:

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

... i żebyś mógł łatwiej to przeczytać:

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
źródło
2
prawdopodobnie dlatego, że jest taki sam jak odpowiedź CMS tylko z funkcjami strzałek
Erich
11

oto działające skrzypce, które działa dobrze w IE8 przy użyciu funkcji MAP 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;
});
Chetan Sandeep Renu
źródło
7

Możesz używać jQuery.grep () od 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
});
Akira Yamamoto
źródło
7

Można to zrobić dość łatwo - prawdopodobnie istnieje wiele implementacji do wyboru, ale to mój podstawowy pomysł (i prawdopodobnie istnieje pewien format, w którym można iterować obiekt za pomocą jQuery, nie mogę teraz o tym pomyśleć):

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

Następnie możesz wywołać tę funkcję w następujący sposób:

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

W ten sposób możesz wywołać filtr na podstawie dowolnego predykatu, który zdefiniujesz, a nawet filtrować wiele razy, używając mniejszych filtrów.

Tejs
źródło
Zmień „int length” na „var length” w pierwszym fragmencie
Malay Desai
4

Możesz samodzielnie wdrożyć metodę filtrowania, która spełnia Twoje potrzeby, oto jak:

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

Mam nadzieję, że to pomoże!

użytkownik4620852
źródło
Mój stan predykatu zmienia się w zależności od różnych kliknięć. Jak przechowywać bieżące wartości predykatów i stosować je do funkcji mojego filtru, kiedy tylko chcę?
Malay Desai
3

Powinieneś sprawdzić OGX.List, który ma wbudowane metody filtrowania i rozszerza standardową tablicę javascript (a także grupowanie, sortowanie i wyszukiwanie). Oto lista operatorów obsługiwanych przez filtry:

'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

Możesz użyć tego w ten sposób

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

Lub nawet w ten sposób

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

Lub jako jedna wkładka

   let filtered_list = new OGX.List(your_array).get({price:{btw:[100,500]}, sqft:{gte:500}});
Eric
źródło
2

Możesz też po prostu użyć $.each(który działa również dla obiektów, nie tylko tablic) i zbudować nową tablicę:

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
źródło
2

Używam mojej ruleOutfunkcji do filtrowania obiektów na podstawie określonych niepożądanych wartości właściwości. Rozumiem, że w twoim przykładzie chciałbyś użyć warunków zamiast wartości, ale moja odpowiedź jest ważna dla tytułu pytania, więc chciałbym zostawić tutaj swoją metodę.

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

Załóżmy, że masz listę takich aktorów:

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

i chcielibyście znaleźć wszystkich aktorów, którzy zostali sklasyfikowani jako gwiazdy Holywood, ich narodowość nie powinna należeć do „angielskiej”, „włoskiej”, „hiszpańskiej”, „greckiej”, a ich imiona nie będą należeć do „Mary”, „Joe”. Przykład Bizzara, wiem! W każdym razie przy takim zestawie warunków utworzyłbyś następujący obiekt:

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

OK, teraz jeśli tylko ruleOut(actors, unwantedFieldsFilter)byś to dostał

[{userName: „Bill”, stanowisko: „star”, język: „Turkish”}]

A Bill jest twoim mężczyzną, ponieważ nie ma na imię „Mary”, „Joe”, jego narodowość nie jest uwzględniona w [„angielskim”, „włoskim”, „hiszpańskim”, „greckim”], a ponadto jest gwiazdą!

W mojej metodzie jest jedna opcja, która jest applyAllFiltersi jest domyślnie prawdziwa. Jeśli spróbujesz ustawić regułę z tym parametrem ustawionym na false, zadziała to jak filtrowanie „LUB” zamiast „I”. Przykład: ruleOut(actors, {job:["actor"], language:["Italian"]}, false)sprawiłby, że każdy, kto nie jest aktorem ani Włochem:

[{nazwa użytkownika: „Mary”, praca: „gwiazda”, język: „turecki”},
{nazwa użytkownika: „Takis”, praca: „gwiazda”, język: „grecki”},
{nazwa użytkownika: „Joe”, praca: „star”, język: „turecki”},
{userName: „Bill”, stanowisko: „star”, język: „turecki”}]

JohnPan
źródło
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);

Możesz użyć tej funkcji lambda. Więcej szczegółów można znaleźć tutaj, ponieważ filtrujemy dane w oparciu o twoje warunki, które zwracają wartość prawda lub fałsz, a dane będą gromadzone w innej tablicy, więc twoja rzeczywista tablica nie zostanie zmodyfikowana.

@JGreig Proszę spojrzeć na to.

hardik beladiya
źródło
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
źródło
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
źródło