javascript过滤数组多个条件

Posted

技术标签:

【中文标题】javascript过滤数组多个条件【英文标题】:javascript filter array multiple conditions 【发布时间】:2015-10-28 04:17:08 【问题描述】:

我想简化一个对象数组。假设我有以下数组:

var users = [
    name: 'John',
    email: 'johnson@mail.com',
    age: 25,
    address: 'USA'
    ,
    
        name: 'Tom',
        email: 'tom@mail.com',
        age: 35,
        address: 'England'
    ,
    
        name: 'Mark',
        email: 'mark@mail.com',
        age: 28,
        address: 'England'
];

和过滤对象:

var filter = address: 'England', name: 'Mark';

例如,我需要按地址和名称过滤所有用户,所以我会遍历过滤器对象属性并检查它:

function filterUsers (users, filter) 
    var result = [];
    for (var prop in filter) 
        if (filter.hasOwnProperty(prop)) 

            //at the first iteration prop will be address
            for (var i = 0; i < filter.length; i++) 
                if (users[i][prop] === filter[prop]) 
                    result.push(users[i]);
                
            
        
    
    return result;

因此,在第一次迭代中,prop - address 将等于 'England' 两个用户将被添加到数组结果中(名称为 Tom 和 Mark),但在第二次迭代中,prop name 将等于 Mark 只有最后一个用户应该添加到数组结果中,但我最终在数组中有两个元素。

我对为什么会发生这种情况有所了解,但仍然卡在它上面并且找不到修复它的好方法。任何帮助都是可观的。谢谢。

【问题讨论】:

为什么要循环访问用户 2 次? 【参考方案1】:

你可以这样做

var filter = 
  address: 'England',
  name: 'Mark'
;
var users = [
    name: 'John',
    email: 'johnson@mail.com',
    age: 25,
    address: 'USA'
  ,
  
    name: 'Tom',
    email: 'tom@mail.com',
    age: 35,
    address: 'England'
  ,
  
    name: 'Mark',
    email: 'mark@mail.com',
    age: 28,
    address: 'England'
  
];


users= users.filter(function(item) 
  for (var key in filter) 
    if (item[key] === undefined || item[key] != filter[key])
      return false;
  
  return true;
);

console.log(users)

【讨论】:

这很好,但实际上我得到了 Uncaught ReferenceError: filter is not defined 您应该为过滤器变量选择不同的名称。为变量提供受保护的名称是一种不好的编码习惯。特别是因为您在几行之后最终使用了该受保护的名称来调用 Array.prototype.filter 方法。 很好的答案。当您想根据多个条件进行过滤时,例如: var filter_address = ['England', 'UK']; var filter_name = ['汤姆','安妮']; ? @andrussk var country = ["England", "UK"], names = ["Tom", "Anne"]; users.filter(el => (country.indexOf(el.address) >= 0 && names.indexOf(el.name) >=0 ) ); 如果过滤条件是数组呢?【参考方案2】:

如果你知道过滤器的名称,你可以在一行中完成。

users = users.filter(obj => obj.name == filter.name && obj.address == filter.address)

【讨论】:

这假设您有姓名和地址作为过滤器 @EddieFreeman 如果您不知道应该使用哪些过滤器,您将如何解决这个问题?【参考方案3】:

喜欢简洁代码的人的另一种选择。

注意FILTER 方法可以采用额外的 this 参数,然后使用 E6 箭头函数我们可以重用正确的 得到一个不错的单线。

var users = [name: 'John',email: 'johnson@mail.com',age: 25,address: 'USA',
             name: 'Tom',email: 'tom@mail.com',age: 35,address: 'England',
             name: 'Mark',email: 'mark@mail.com',age: 28,address: 'England'];

var query = address: "England", name: "Mark";

var result = users.filter(search, query);

function search(user)
  return Object.keys(this).every((key) => user[key] === this[key]);





// |----------------------- Code for displaying results -----------------|
var element = document.getElementById('result');

function createMarkUp(data)
  Object.keys(query).forEach(function(key)
    var p = document.createElement('p');
    p.appendChild(document.createTextNode(
    key.toUpperCase() + ': ' + result[0][key]));
    element.appendChild(p);
  );


createMarkUp(result);
&lt;div id="result"&gt;&lt;/div&gt;

【讨论】:

【参考方案4】:

这里是在过滤器中使用箭头函数的 ES6 版本。发布这个作为答案是因为我们现在大多数人都在使用 ES6,并且可以帮助读者使用箭头函数、let 和 const 以高级方式进行过滤。

const filter = 
  address: 'England',
  name: 'Mark'
;
let users = [
    name: 'John',
    email: 'johnson@mail.com',
    age: 25,
    address: 'USA'
  ,
  
    name: 'Tom',
    email: 'tom@mail.com',
    age: 35,
    address: 'England'
  ,
  
    name: 'Mark',
    email: 'mark@mail.com',
    age: 28,
    address: 'England'
  
];


users= users.filter(item => 
  for (let key in filter) 
    if (item[key] === undefined || item[key] != filter[key])
      return false;
  
  return true;
);

console.log(users)

【讨论】:

【参考方案5】:

使用Array.Filter() 和Arrow Functions 我们可以实现这一点

users = users.filter(x => x.name == 'Mark' && x.address == 'England');

这里是完整的sn-p

// initializing list of users
var users = [
    name: 'John',
    email: 'johnson@mail.com',
    age: 25,
    address: 'USA'
  ,
  
    name: 'Tom',
    email: 'tom@mail.com',
    age: 35,
    address: 'England'
  ,
  
    name: 'Mark',
    email: 'mark@mail.com',
    age: 28,
    address: 'England'
  
];

//filtering the users array and saving 
//result back in users variable
users = users.filter(x => x.name == 'Mark' && x.address == 'England');


//logging out the result in console
console.log(users);

【讨论】:

比较 x.name == 'Mark' 可能会导致意外的类型强制【参考方案6】:

也可以这样:

    this.users = this.users.filter((item) => 
                return (item.name.toString().toLowerCase().indexOf(val.toLowerCase()) > -1 ||
                item.address.toLowerCase().indexOf(val.toLowerCase()) > -1 ||
                item.age.toLowerCase().indexOf(val.toLowerCase()) > -1 ||
                item.email.toLowerCase().indexOf(val.toLowerCase()) > -1);
            )

【讨论】:

【参考方案7】:

我认为这可能会有所帮助。

const filters = ['a', 'b'];

const results = [
  
    name: 'Result 1',
    category: ['a']
  ,
  
    name: 'Result 2',
    category: ['a', 'b']
  ,
  
    name: 'Result 3',
    category: ['c', 'a', 'b', 'd']
  
];

const filteredResults = results.filter(item =>
  filters.every(val => item.category.indexOf(val) > -1)
);

console.log(filteredResults);
  

【讨论】:

&amp;&amp; 操作员完成了这项工作 你也可以使用Array.every(),我更新了代码。【参考方案8】:

在lodash中,

_.filter(users,address: 'England', name: 'Mark')

在 es6 中,

users.filter(o => o.address == 'England' && o.name == 'Mark')

【讨论】:

【参考方案9】:

带有 AND 条件的动态过滤器

过滤掉性别='m'的人

var people = [
    
        name: 'john',
        age: 10,
        gender: 'm'
    ,
    
        name: 'joseph',
        age: 12,
        gender: 'm'
    ,
    
        name: 'annie',
        age: 8,
        gender: 'f'
    
]
var filters = 
    gender: 'm'


var out = people.filter(person => 
    return Object.keys(filters).every(filter => 
        return filters[filter] === person[filter]
    );
)


console.log(out)

过滤掉性别 = 'm' 和姓名 = 'joseph' 的人

var people = [
    
        name: 'john',
        age: 10,
        gender: 'm'
    ,
    
        name: 'joseph',
        age: 12,
        gender: 'm'
    ,
    
        name: 'annie',
        age: 8,
        gender: 'f'
    
]
var filters = 
    gender: 'm',
    name: 'joseph'


var out = people.filter(person => 
    return Object.keys(filters).every(filter => 
        return filters[filter] === person[filter]
    );
)


console.log(out)

您可以提供任意数量的过滤器。

【讨论】:

【参考方案10】:

如果要在filter 中放入多个条件,可以使用&amp;&amp;|| 运算符。

var product= Object.values(arr_products).filter(x => x.Status==status && x.email==user)

【讨论】:

【参考方案11】:

改进这里的好答案,下面是我的解决方案:

const rawData = [
   name: 'John', email: 'johnson@mail.com', age: 25, address: 'USA' ,
   name: 'Tom', email: 'tom@mail.com', age: 35, address: 'England' ,
   name: 'Mark', email: 'mark@mail.com', age: 28, address: 'England' 
]
const filters =  address: 'England', age: 28 

const filteredData = rawData.filter(i =>
  Object.entries(filters).every(([k, v]) => i[k] === v)
)

【讨论】:

【参考方案12】:

功能解决方案

function applyFilters(data, filters) 
  return data.filter(item =>
    Object.keys(filters)
      .map(keyToFilterOn =>
        item[keyToFilterOn].includes(filters[keyToFilterOn]),
      )
      .reduce((x, y) => x && y, true),
  );

这应该可以完成工作

applyFilters(users, filter);

【讨论】:

【参考方案13】:

如果您将过滤器对象中的值转换为数组,您将拥有更大的灵活性:

var filter = address: ['England'], name: ['Mark'] ;

这样您就可以过滤“英格兰”或“苏格兰”之类的内容,这意味着结果可能包括英格兰和苏格兰的记录:

var filter = address: ['England', 'Scotland'], name: ['Mark'] ;

通过该设置,您的过滤功能可以是:

const applyFilter = (data, filter) => data.filter(obj =>
    Object.entries(filter).every(([prop, find]) => find.includes(obj[prop]))
);

// demo
var users = [name: 'John',email: 'johnson@mail.com',age: 25,address: 'USA',name: 'Tom',email: 'tom@mail.com',age: 35,address: 'England',name: 'Mark',email: 'mark@mail.com',age: 28,address: 'England'];var filter = address: ['England'], name: ['Mark'] ;
var filter = address: ['England'], name: ['Mark'] ;

console.log(applyFilter(users, filter));

【讨论】:

【参考方案14】:

干净实用的解决方案

const combineFilters = (...filters) => (item) => 
    return filters.map((filter) => filter(item)).every((x) => x === true);
;

然后你像这样使用它:

const filteredArray = arr.filter(combineFilters(filterFunc1, filterFunc2));

例如 filterFunc1 可能如下所示:

const filterFunc1 = (item) => 
  return item === 1 ? true : false;
;

【讨论】:

【参考方案15】:

我的解决方案,基于 NIKHIL C M 解决方案:

 let data = [
     
      key1: "valueA1", 
      key2: "valueA2",
      key3: []
    ,
      key1: "valueB1", 
      key2: "valueB2"
      key3: ["valuesB3"]
    
 ];

 let filters = 
    key1: "valueB1",
    key2: "valueB2"
 ;

 let filteredData = data.filter((item) => 
     return Object.entries(filters).every(([filter, value]) => 
          return item[filter] === value;
          //Here i am applying a bit more logic like 
          //return item[filter].includes(value) 
          //or filter with not exactly same key name like
          //return !isEmpty(item.key3)
     );
 );

【讨论】:

【参考方案16】:

如果您的代码的最终目的是获取过滤后的用户,我将反转 for 以评估 user,而不是在每次迭代期间减少结果数组。

这是一个(未经测试的)示例:

function filterUsers (users, filter) 
    var result = [];

    for (i=0;i<users.length;i++)
        for (var prop in filter) 
            if (users.hasOwnProperty(prop) && users[i][prop] === filter[prop]) 
                result.push(users[i]);
            
        
    
    return result;

【讨论】:

【参考方案17】:

由一些小帮手组成:

const filter = address: 'England', name: 'Mark';
console.log( 
  users.filter(and(map(propMatches)(filter)))
)

function propMatches<T>(property: string, value: any) 
  return (item: T): boolean => item[property] === value


function map<T>(mapper: (key: string, value: any, obj: T) => (item:T) => any) 
  return (obj: T) => 
    return Object.keys(obj).map((key) => 
      return mapper(key, obj[key], obj)
    );
  


export function and<T>(predicates: ((item: T) => boolean)[]) 
  return (item: T) =>
    predicates.reduce(
        (acc: boolean, predicate: (item: T) => boolean) => 
            if (acc === undefined) 
                return !!predicate(item);
            
            return !!predicate(item) && acc;
        ,
        undefined // initial accumulator value
    );

【讨论】:

这是什么语言?这是一个 javascript 问题。 不,不是。 SyntaxError: Unexpected token '&lt;' 这是打字稿。【参考方案18】:

这是一个易于理解的功能解决方案

let filtersObject = 
  address: "England",
  name: "Mark"
;

let users = [
    name: 'John',
    email: 'johnson@mail.com',
    age: 25,
    address: 'USA'
  ,
  
    name: 'Tom',
    email: 'tom@mail.com',
    age: 35,
    address: 'England'
  ,
  
    name: 'Mark',
    email: 'mark@mail.com',
    age: 28,
    address: 'England'
  
];

function filterUsers(users, filtersObject) 
  //Loop through all key-value pairs in filtersObject
  Object.keys(filtersObject).forEach(function(key) 
    //Loop through users array checking each userObject
    users = users.filter(function(userObject) 
      //If userObject's key:value is same as filtersObject's key:value, they stay in users array
      return userObject[key] === filtersObject[key]
    )
  );
  return users;


//ES6
function filterUsersES(users, filtersObject) 
  for (let key in filtersObject) 
    users = users.filter((userObject) => userObject[key] === filtersObject[key]);
  
  return users;


console.log(filterUsers(users, filtersObject));
console.log(filterUsersES(users, filtersObject));

【讨论】:

【参考方案19】:

这是我想出的另一种方法,其中 filtersUsers 是一个返回已排序用户列表的函数。

var filtersample = address: 'England', name: 'Mark';

filteredUsers() 
  return this.users.filter((element) => 
    return element['address'].toLowerCase().match(this.filtersample['address'].toLowerCase()) || element['name'].toLowerCase().match(this.filtersample['name'].toLowerCase());
  )

【讨论】:

【参考方案20】:
const users = [
    name: 'John',
    email: 'johnson@mail.com',
    age: 25,
    address: 'USA'
  ,
  
    name: 'Tom',
    email: 'tom@mail.com',
    age: 35,
    address: 'England'
  ,
  
    name: 'Mark',
    email: 'mark@mail.com',
    age: 28,
    address: 'England'
  
];

const filteredUsers = users.filter(( name, age ) => name === 'Tom' && age === 35)

console.log(filteredUsers)

【讨论】:

虽然此代码可能会为问题提供解决方案,但最好添加有关其工作原理/方式的上下文。这可以帮助未来的用户学习并将这些知识应用到他们自己的代码中。在解释代码时,您也可能会以赞成票的形式从用户那里获得积极的反馈。【参考方案21】:

使用 lodash 而不是纯 javascript

这实际上使用 lodash 非常简单,并且非常容易添加/修改过滤器。

import _ from 'lodash';

async getUsersWithFilter(filters) 
     const users = yourArrayOfSomethingReally();

    // Some properties of the 'filters' object can be null or undefined, so create a new object without those undefined properties and filter by those who are defined
    const filtersWithoutUndefinedValuesObject = _.omitBy(
      filters,
      _.isNil,
    );

    return _.filter(users,  ...filtersWithoutUndefinedValuesObject );

    omitBy 函数检查您的 filters 对象并删除任何为 null 或未定义的值(如果将其取出,lodash.filter 函数不会返回任何结果。

    filter 函数将过滤掉所有值与您作为第二个参数传递给函数的对象不匹配的对象(在本例中,就是您的 filters 对象。)

为什么要使用这个?

好吧,假设你有这个对象:

const myFiltersObj = 

   name: "Java",
   age: 50

;

如果您想添加另一个过滤器,只需向 myFilterObj 添加一个新属性,如下所示:

const myFiltersObj = 

   name: "Java",
   email: 50,
   country: "HND"

;

调用 getUsersWithFilter 函数,它会正常工作。如果你跳过,比如说对象中的 name 属性,getUsersWithFilter 函数将按电子邮件和国家/地区过滤就好了。

【讨论】:

【参考方案22】:

请用您提供的数据检查下面的代码sn-p,它将返回基于多列过滤的数据。

var filter = 
  address: 'India',
  age: '27'
;

var users = [
    name: 'Nikhil',
    email: 'nikhil@mail.com',
    age: 27,
    address: 'India'
  ,
  
    name: 'Minal',
    email: 'minal@mail.com',
    age: 27,
    address: 'India'
  ,
  
    name: 'John',
    email: 'johnson@mail.com',
    age: 25,
    address: 'USA'
  ,
  
    name: 'Tom',
    email: 'tom@mail.com',
    age: 35,
    address: 'England'
  ,
  
    name: 'Mark',
    email: 'mark@mail.com',
    age: 28,
    address: 'England'
  
];


function filterByMultipleColumns(users, columnDataToFilter) 
  return users.filter(row => 
    return Object.keys(columnDataToFilter).every(propertyName => row[propertyName].toString().toLowerCase().indexOf(columnDataToFilter[propertyName].toString().toLowerCase()) > -1);
  )


var filteredData = filterByMultipleColumns(users, filter);

console.log(filteredData);

结果: [ "name": "Nikhil", "email": "nikhil@mail.com", "age": 27, "address": "India" , "name": "Minal", "email": "minal@mail.com", "age": 27, "address": "India" ]

请检查下面的链接,只需稍作改动即可使用 Javascript filter array multiple values – example

【讨论】:

【参考方案23】:

我正在回答的一个问题被(正确地)关闭为这个问题的副本。但是我看不到上面的任何答案都像这个答案。所以这里还有一个选择。

我们可以编写一个简单的函数,它接受诸如name: 'mike', house: 'blue' 之类的规范,并返回一个函数,该函数将测试传递给它的值是否与所有属性匹配。可以这样使用:

const where = (spec, entries = Object .entries (spec)) => (x) =>
  entries .every (([k, v]) => x [k] == v)

const users = [name: 'John', email: 'johnson@mail.com', age: 25, address: 'USA', name: 'Mark', email: 'marcus@mail.com', age: 25, address: 'USA', name: 'Tom', email: 'tom@mail.com', age: 35, address: 'England', name: 'Mark', email: 'mark@mail.com', age: 28, address: 'England']

console .log ('Mark', users .filter (where (name: 'Mark')))
console .log ('England', users .filter (where (address: 'England')))
console .log ('Mark/England', users .filter (where (name: 'Mark', address: 'England')))
.as-console-wrapper max-height: 100% !important; top: 0

如果我们想将过滤封装到一个函数中,我们可以重用同一个函数,封装如下:

const where = (spec, entries = Object .entries (spec)) => (x) =>
  entries .every (([k, v]) => x [k] == v)

const filterBy = (spec) => (xs) => 
  xs .filter (where (spec))

const users = [name: 'John', email: 'johnson@mail.com', age: 25, address: 'USA', name: 'Mark', email: 'marcus@mail.com', age: 25, address: 'USA', name: 'Tom', email: 'tom@mail.com', age: 35, address: 'England', name: 'Mark', email: 'mark@mail.com', age: 28, address: 'England']


console .log ('Mark/England', filterBy (address: "England", name: "Mark") (users))
.as-console-wrapper max-height: 100% !important; top: 0

(当然最后一个不必是柯里化的。我们可以改变它,这样我们就可以一次用两个参数调用它。我觉得这更灵活,但是 YMMV。)

将它作为一个单独的函数保存的好处是我们可以重用它,例如 find 或其他匹配情况。


这种设计与Ramda 中where 的使用非常相似(免责声明:我是Ramda 的作者之一。)Ramda 提供了额外的灵活性,允许使用任意谓词而不是必须相等的值。所以在 Ramda 中,你可能会这样写:

filter (where (
  address: equals ('England')
  age: greaterThan (25)
) (users)

这个想法大致相同,只是更灵活一点。

【讨论】:

【参考方案24】:
const data = [
    realName: 'Sean Bean',
    characterName: 'Eddard “Ned” Stark'
, 
    realName: 'Kit Harington',
    characterName: 'Jon Snow'
, 
    realName: 'Peter Dinklage',
    characterName: 'Tyrion Lannister'
, 
    realName: 'Lena Headey',
    characterName: 'Cersei Lannister'
, 
    realName: 'Michelle Fairley',
    characterName: 'Catelyn Stark'
, 
    realName: 'Nikolaj Coster-Waldau',
    characterName: 'Jaime Lannister'
, 
    realName: 'Maisie Williams',
    characterName: 'Arya Stark'
];

const filterKeys = ['realName', 'characterName'];


const multiFilter = (data = [], filterKeys = [], value = '') => data.filter((item) => filterKeys.some(key => item[key].toString().toLowerCase().includes(value.toLowerCase()) && item[key]));


let filteredData = multiFilter(data, filterKeys, 'stark');

console.info(filteredData);
/* [
  "realName": "Sean Bean",
  "characterName": "Eddard “Ned” Stark"
, 
  "realName": "Michelle Fairley",
  "characterName": "Catelyn Stark"
, 
  "realName": "Maisie Williams",
  "characterName": "Arya Stark"
]
 */

【讨论】:

【参考方案25】:
arr.filter((item) => 
       if(condition)
       
         return false;
       
       return true;
    );

【讨论】:

虽然此代码可能会回答问题,但提供有关它如何和/或为什么解决问题的额外上下文将提高​​答案的长期价值 检查多个条件并在JS过滤器中返回true或false

以上是关于javascript过滤数组多个条件的主要内容,如果未能解决你的问题,请参考以下文章

javascript 过滤具有多个条件的对象数组。

javascript 根据选择条件过滤数组对象。

通过在嵌套的对象数组中查找多个条件来过滤数组

使用Javascript比较(过滤)动态数组中的多个数组值

JavaScript常用数组元素搜索或过滤的四种方法

JavaScript常用数组元素搜索或过滤的四种方法