¿Cómo filtrar una matriz de objetos basada en atributos?


473

Tengo la siguiente matriz de JavaScript de objetos de inicio de bienes raíces:

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;

Lo que me gustaría hacer es poder realizar un filtro en el objeto para devolver un subconjunto de objetos "de inicio".

Por ejemplo, quiero ser capaz de filtro basado en: price, sqft, num_of_beds, y num_of_baths.

¿Cómo puedo realizar algo en JavaScript como el pseudocódigo siguiente?

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

Tenga en cuenta que la sintaxis no tiene que ser exactamente como la anterior. Esto es solo un ejemplo.


77

3
var json = { ... }JSON es una notación textual para el intercambio de datos. (Más información aquí.) Si se trata de código fuente JavaScript y no se trata de una cadena , no se trata de JSON.
TJ Crowder

1
No uses eval. Generalmente es una mala práctica y puede causar problemas de rendimiento. Simplemente tuvimos que deshacernos de un grupo de personas en un proyecto porque el procesador estaba bloqueado.
SDH

Respuestas:


719

Puedes usar el Array.prototype.filtermétodo:

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

Ejemplo en vivo:

Este método es parte del nuevo estándar ECMAScript 5th Edition y se puede encontrar en casi todos los navegadores modernos.

Para IE, puede incluir el siguiente método de compatibilidad:

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
¿Qué es / *, thisp * /?
JGreig

2
@JGreig: es solo un comentario para indicar que se puede pasar un argumento opcional, el argumento no se especifica directamente porque el estándar ECMA dice con precisión que este método debe esperar solo un argumento ( Array.prototype.filter.length == 1;). Cuando use el segundo argumento, se usará como el thisvalor dentro de la función de devolución de llamada.
CMS

1
@CMS, ¿estás seguro de que este código funciona? Esto está devolviendo una matriz vacía, incluso cuando configuré el precio muy alto y los pies cuadrados / camas / baños muy bajos. ¿Estás seguro de que este código funciona?
JGreig

2
@JGreig: Sí, funciona, debería verificar sus criterios, tal vez podría publicar un ejemplo más completo de su JSON en pastie.org o jsbin.com y los criterios que está utilizando para filtrar, para que pueda ayudarlo mejor.
CMS

2
@CMS Sería bueno si la respuesta incluye el hecho de que se devuelve una nueva matriz, la matriz original no se modifica. A pesar de que esto se dice en el enlace proporcionado, encuentro que la respuesta sin esto es incompleta o inexacta
GWorking

29

Puede intentar usar un marco como jLinq: a continuación se muestra un código de muestra del uso de jLinq

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

Para obtener más información, puede seguir el enlace http://www.hugoware.net/projects/jlinq


26

Prefiero el marco de subrayado. Sugiere muchas operaciones útiles con objetos. Tu tarea:

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

se puede sobrescribir como:

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

¡Espero que te sea útil!


¿Cómo funciona esta cosa de subrayado y qué significa exactamente?
John Demetriou

44
Recién descubierto underscore-query( github.com/davidgtonge/underscore-query ) que usa una sintaxis similar a MongoDB para consultar matrices javascript. Así que aquí usarías_.query(homes, {price: {$lt:1000}, sqft: {$gte: 500}, num_of_beds: {$gte:2}, num_of_baths: {$gte: 2.5}}
prototipo del

12

Me sorprende que nadie haya publicado la respuesta de una línea:

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

... y para que puedas leerlo más fácilmente:

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

2
probablemente porque es lo mismo que la respuesta de CMS solo con las funciones de flecha
Erich

11

Aquí está el violín de trabajo que funciona bien en IE8 usando la función 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;
});

7

Puede usar jQuery.grep () desde 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

Podría hacerlo con bastante facilidad: probablemente hay muchas implementaciones entre las que puede elegir, pero esta es mi idea básica (y probablemente haya algún formato en el que pueda iterar sobre un objeto con jQuery, simplemente no puedo pensar en eso ahora):

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

Y luego podría invocar esta función así:

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 esta manera, puede invocar el filtro en función del predicado que defina, o incluso filtrar varias veces utilizando filtros más pequeños.


Cambie "int length" a "var length" en el primer fragmento
Malayo Desai

4

Puede implementar un método de filtro usted mismo que satisfaga sus necesidades, así es como:

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

¡Espero eso ayude!


Mi condición de predicado cambia de varios onclicks. ¿Cómo puedo almacenar los valores de predicado actuales y aplicarlos a la función myfilter cuando quiera?
Malayo Desai

3

Debe consultar OGX.List, que ha incorporado métodos de filtrado y extiende la matriz estándar de JavaScript (y también la agrupación, clasificación y búsqueda). Aquí hay una lista de operadores que admite para los filtros:

'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

Puedes usarlo de esta manera

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

O incluso de esta manera

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

O como un trazador de líneas

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

2

O simplemente puede usar $.each(que también funciona para objetos, no solo matrices) y construir una nueva matriz de esta manera:

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 mi ruleOutfunción para filtrar objetos basados ​​en valores de propiedad no deseados específicos. Entiendo que en su ejemplo le gustaría usar condiciones en lugar de valores, pero mi respuesta es válida para el título de la pregunta, por lo que me gustaría dejar mi método aquí.

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

Digamos que tiene una lista de actores como este:

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

y le gustaría encontrar a todos los actores calificados como estrellas de Holywood, su nacionalidad no debe ser una de 'inglés', 'italiano', 'español', 'griego', además su nombre no sería una de 'Mary', 'Joe'. Bizzar ejemplo, lo sé! De todos modos, con ese conjunto de condiciones crearía el siguiente objeto:

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

Bien, ahora si ruleOut(actors, unwantedFieldsFilter)solo tuvieras

[{userName: "Bill", trabajo: "star", idioma: "turco"}]

¡Y Bill es tu hombre, ya que su nombre no es uno de 'Mary', 'Joe', su nacionalidad no está incluida en ['inglés', 'italiano', 'español', 'griego'] y además es una estrella!

Hay una opción en mi método, que es applyAllFiltersy es verdadera por defecto. Si intentara descartar con este parámetro configurado como falso, funcionaría como un filtro 'OR' en lugar de 'AND'. Ejemplo: ruleOut(actors, {job:["actor"], language:["Italian"]}, false)te llevaría a todos los que no sean actores o italianos:

[{userName: "Mary", trabajo: "star", idioma: "turco"},
{userName: "Takis", trabajo: "star", idioma: "griego"},
{userName: "Joe", trabajo: "star", idioma: "turco"},
{userName: "Bill", trabajo: "star", idioma: "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);

Puedes usar esta función lambda. Puede encontrar más detalles aquí ya que estamos filtrando los datos en función de su condición que devuelve verdadero o falso y recopilará los datos en una matriz diferente para que su matriz real no se modifique.

@JGreig Por favor, míralo.


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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.