Comment filtrer le tableau d'objets en fonction des attributs?

473

J'ai le tableau JavaScript suivant des objets immobiliers à la maison:

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;

Ce que je voudrais faire, c'est pouvoir effectuer un filtre sur l'objet pour renvoyer un sous-ensemble d'objets "home".

Par exemple, je veux être en mesure de filtrer en fonction: price, sqft, num_of_bedset num_of_baths.

Comment puis-je effectuer quelque chose en JavaScript comme le pseudo-code ci-dessous:

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

Notez que la syntaxe ne doit pas être exactement comme ci-dessus. C'est juste un exemple.

JGreig
la source
7
Cela semble presque identique à stackoverflow.com/questions/1694717/…
Crescent Fresh
3
var json = { ... }JSON est une notation textuelle pour l'échange de données. (Plus ici.) Si vous traitez avec du code source JavaScript et non avec une chaîne , vous ne traitez pas avec JSON.
TJ Crowder
1
N'utilisez pas eval. C'est généralement une mauvaise pratique et peut entraîner des problèmes de performances. Nous avons juste dû nous débarrasser d'un tas de ceux sur un projet parce que le processeur se bloquait.
SDH

Réponses:

719

Vous pouvez utiliser la Array.prototype.filterméthode:

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

Exemple en direct:

Cette méthode fait partie de la nouvelle norme ECMAScript 5e édition , et peut être trouvée sur presque tous les navigateurs modernes.

Pour IE, vous pouvez inclure la méthode de compatibilité suivante:

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
la source
8
qu'est-ce que / *, thisp * /?
JGreig
2
@JGreig: est juste un commentaire pour indiquer qu'un argument optionnel peut être passé, l'argument n'est pas spécifié directement parce que la norme ECMA dit précisément que cette méthode ne doit s'attendre qu'à un seul argument ( Array.prototype.filter.length == 1;). Lorsque vous utilisez le deuxième argument, il sera utilisé comme thisvaleur dans la fonction de rappel.
CMS
1
@CMS, êtes-vous sûr que ce code fonctionne? Cela renvoie un tableau vide, même lorsque je fixe le prix à un prix vraiment élevé et que les pieds carrés / lits / bains sont vraiment bas. Êtes-vous sûr que ce code fonctionne?
JGreig
2
@JGreig: Oui, cela fonctionne, vous devriez vérifier vos critères, peut-être pourriez-vous publier un exemple plus complet de votre JSON dans pastie.org ou jsbin.com et les critères que vous utilisez pour filtrer, afin que je puisse mieux vous aider.
CMS
2
@CMS Ce serait bien si la réponse inclut le fait qu'un nouveau tableau est retourné, le tableau d'origine n'est pas modifié. Même si cela est dit dans le lien fourni, je trouve que la réponse sans que cela soit incomplet ou inexact
GWorking
29

Vous pouvez essayer d'utiliser un framework comme jLinq - voici un exemple de code d'utilisation de jLinq

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

Pour plus d'informations, vous pouvez suivre le lien http://www.hugoware.net/projects/jlinq

Rutesh Makhijani
la source
26

Je préfère le cadre Underscore. Il suggère de nombreuses opérations utiles avec des objets. Ta tâche:

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

peut être écrasé comme:

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

J'espère que cela vous sera utile!

JuliaCesar
la source
Comment cela fonctionne-t-il et que signifie-t-il exactement?
John Demetriou
4
Je viens de découvrir underscore-query( github.com/davidgtonge/underscore-query ) qui utilise une syntaxe de type MongoDB pour interroger les tableaux javascript. Donc, ici, vous utiliseriez_.query(homes, {price: {$lt:1000}, sqft: {$gte: 500}, num_of_beds: {$gte:2}, num_of_baths: {$gte: 2.5}}
prototype
12

Je suis surpris que personne n'ait publié la réponse en ligne:

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

... et pour que vous puissiez le lire plus facilement:

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
la source
2
probablement parce que c'est la même chose que la réponse de CMS juste avec les fonctions fléchées
Erich
11

voici le violon qui fonctionne bien dans IE8 en utilisant la fonction jquery MAP

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
la source
7

Vous pouvez utiliser jQuery.grep () depuis 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
la source
7

Vous pouvez le faire assez facilement - il y a probablement de nombreuses implémentations parmi lesquelles vous pouvez choisir, mais c'est mon idée de base (et il y a probablement un format où vous pouvez parcourir un objet avec jQuery, je ne peux pas y penser en ce moment):

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

Et puis vous pouvez invoquer cette fonction comme ceci:

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

De cette façon, vous pouvez invoquer le filtre en fonction du prédicat que vous définissez, ou même filtrer plusieurs fois à l'aide de filtres plus petits.

Tejs
la source
Veuillez modifier "int length" en "var length" dans le premier extrait
Malay Desai
4

Vous pouvez implémenter vous-même une méthode de filtrage qui répond à vos besoins, voici comment:

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

J'espère que cela aide!

user4620852
la source
Ma condition de prédicat change de divers onclicks. Comment peut-on stocker les valeurs de prédicat actuelles et les appliquer à la fonction myfilter quand je le souhaite?
Malay Desai
3

Vous devriez vérifier OGX.List qui a intégré des méthodes de filtrage et étend le tableau javascript standard (ainsi que le regroupement, le tri et la recherche). Voici une liste des opérateurs qu'il prend en charge pour les filtres:

'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

Vous pouvez l'utiliser de cette façon

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

Ou même de cette façon

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

Ou comme un liner

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

Ou vous pouvez simplement utiliser $.each(qui fonctionne également pour les objets, pas seulement les tableaux) et créer un nouveau tableau comme ceci:

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
la source
2

J'utilise ma ruleOutfonction pour filtrer les objets en fonction de valeurs de propriétés indésirables spécifiques. Je comprends que dans votre exemple, vous souhaitez utiliser des conditions au lieu de valeurs, mais ma réponse est valide pour le titre de la question, je voudrais donc laisser ma méthode ici.

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

Disons que vous avez une liste d'acteurs comme celui-ci:

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

et vous souhaitez trouver tous les acteurs qui sont considérés comme des stars de Holywood, leur nationalité ne doit pas être «anglais», «italien», «espagnol», «grec», plus leur nom ne doit pas être «Mary», «Joe». Exemple Bizzar, je sais! Quoi qu'il en soit, avec cet ensemble de conditions, vous créeriez l'objet suivant:

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

OK, maintenant si vous ruleOut(actors, unwantedFieldsFilter)vous

[{userName: "Bill", travail: "star", langue: "turc"}]

Et Bill est votre homme, puisque son nom n'est pas celui de 'Mary', 'Joe', sa nationalité n'est pas incluse dans ['Anglais', 'Italien', 'Espagnol', 'Grec'] plus c'est une Star!

Il y a une option dans ma méthode, c'est applyAllFilterset c'est vrai par défaut. Si vous tentiez de faire un ruleOut avec ce paramètre défini comme faux, cela fonctionnerait comme un filtrage «OU» au lieu de «ET». Exemple: ruleOut(actors, {job:["actor"], language:["Italian"]}, false)vous obtiendrez tout le monde qui n'est pas un acteur ou un italien:

[{userName: "Mary", travail: "star", langue: "turc"},
{userName: "Takis", travail: "star", langue: "grec"},
{userName: "Joe", travail: "star", langue: "turc"},
{userName: "Bill", travail: "star", langue: "turc"}]

JohnPan
la source
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);

Vous pouvez utiliser cette fonction lambda. Plus de détails peuvent être trouvés ici car nous filtrons les données en fonction de votre condition qui retourne vrai ou faux et il collectera les données dans un tableau différent afin que votre tableau ne soit pas modifié.

@JGreig Veuillez l'examiner.

hardik beladiya
la source
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
la source
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
la source