简体   繁体   English

javascript过滤数组多个条件

[英]javascript filter array multiple conditions

I want to simplify an array of objects.我想简化一组对象。 Let's assume that I have following array:假设我有以下数组:

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

And filter object:和过滤对象:

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

For example i need to filter all users by address and name, so i do loop through filter object properties and check it out:例如,我需要按地址和姓名过滤所有用户,所以我循环过滤对象属性并检查它:

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

So during first iteration when prop - address will be equal 'England' two users will be added to array result (with name Tom and Mark), but on the second iteration when prop name will be equal Mark only the last user should be added to array result, but i end up with two elements in array.因此,在第一次迭代中,当prop - address等于'England'时,两个用户将被添加到数组结果(名称为 Tom 和 Mark),但是在第二次迭代中,当prop name等于Mark时,只应将最后一个用户添加到数组结果,但我最终得到数组中的两个元素。

I have got a little idea as why is it happening but still stuck on it and could not find a good solution to fix it.我知道为什么会这样,但仍然坚持下去,找不到好的解决方案来解决它。 Any help is appreciable.任何帮助都是可观的。 Thanks.谢谢。

You can do like this你可以这样做

 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)

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

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

Another take for those of you that enjoy succinct code.对于那些喜欢简洁代码的人来说,这是另一种选择。

NOTE : The FILTER method can take an additional this argument, then using an E6 arrow function we can reuse the correct this to get a nice one-liner.注意FILTER方法可以接受一个额外的this参数,然后使用 E6 箭头函数,我们可以重用正确的this以获得一个不错的单行。

 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);
 <div id="result"></div>

Here is ES6 version of using arrow function in filter.这是在过滤器中使用箭头函数的 ES6 版本。 Posting this as an answer because most of us are using ES6 these days and may help readers to do filter in advanced way using arrow function, let and const.发布这个作为答案是因为我们现在大多数人都在使用 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)

Using Array.Filter() with Arrow Functions we can achieve this usingArray.Filter()箭头函数一起使用,我们可以使用

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

Here is the complete snippet这是完整的片段

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

Can also be done this way:也可以这样做:

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

I think this might help.我认为这可能会有所帮助。

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

Improving on the good answers here, below is my solution:改进这里的好答案,下面是我的解决方案:

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

In lodash,在洛达什,

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

In es6,在 es6 中,

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

Much better for es6. es6 好很多。 or you can use ||或者你可以使用 || (or) operator like this (或)这样的运算符

users.filter(o => {return (o.address == 'England' || o.name == 'Mark')})

If you want to put multiple conditions in filter , you can use && and ||如果要在filter中放置多个条件,可以使用&&|| operator.操作员。

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

Dynamic filters with AND condition带有 AND 条件的动态过滤器

Filter out people with gender = 'm'过滤掉性别 = '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)

Filter out people with gender = 'm' and name = 'joseph'过滤掉性别 = 'm' 和 name = '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)

You can give as many filters as you want.您可以根据需要提供任意数量的过滤器。

functional solution功能解决方案

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

this should do the job这应该做的工作

applyFilters(users, filter);

You'll have more flexibility if you turn the values in your filter object into arrays:如果将过滤器对象中的值转换为数组,您将拥有更大的灵活性:

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

That way you can filter for things like "England" or "Scotland", meaning that results may include records for England, and for Scotland:这样您就可以过滤诸如“England”或“Scotland”之类的内容,这意味着结果可能包括英格兰和苏格兰的记录:

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

With that setup, your filtering function can be:通过该设置,您的过滤功能可以是:

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

A clean and functional solution干净实用的解决方案

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

then you use it like so:然后你像这样使用它:

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

and filterFunc1 for example might look like this:例如 filterFunc1 可能如下所示:

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

My solution, based on NIKHIL CM solution:我的解决方案,基于 NIKHIL CM 解决方案:

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

If the finality of you code is to get the filtered user, I would invert the for to evaluate the user instead of reducing the result array during each iteration.如果您的代码的最终目的是获取过滤后的用户,我会反转for来评估user ,而不是在每次迭代期间减少结果数组。

Here an (untested) example:这是一个(未经测试的)示例:

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

with the composition of some little helpers:由一些小帮手组成:

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

This is an easily understandable functional solution这是一个易于理解的功能解决方案

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

This is another method i figured out, where filteredUsers is a function that returns the sorted list of users.这是我想出的另一种方法,其中filteredUsers 是一个返回已排序用户列表的函数。

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

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)

Using lodash and not pure javascript使用 lodash 而不是纯 JavaScript

This is actually quite simple using lodash and very easy to add/modify filters.这实际上使用 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 });
}
  1. The omitBy function checks your filters object and removes any value that is null or undefined (if you take it out, the lodash.filter function wont return any result. omitBy函数检查您的过滤器对象并删除任何为 null 或未定义的值(如果您将其取出, lodash.filter函数将不会返回任何结果。

  2. The filter function will filter out all the objects who's values don't match with the object you pass as a second argument to the function (which in this case, is your filters object.)过滤器函数将过滤掉所有值与您作为第二个参数传递给函数的对象不匹配的对象(在本例中,它是您的过滤器对象。)

Why use this?为什么要用这个?

Well, assume you have this object:好吧,假设你有这个对象:

const myFiltersObj = {

   name: "Java",
   age: 50

};

If you want to add another filter, just add a new property to the myFilterObj , like this:如果要添加另一个过滤器,只需向myFilterObj添加一个新属性,如下所示:

const myFiltersObj = {

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

};

Call the getUsersWithFilter function, and it will work just fine.调用getUsersWithFilter函数,它就可以正常工作了。 If you skip, let's say the name property in the object, the getUsersWithFilter function will filter by the email and country just fine.如果你跳过,比如说对象中的 name 属性, getUsersWithFilter函数将按电子邮件和国家/地区过滤就好了。

Please check below code snippet with data you provided, it will return filtered data on the basis of multiple columns.请使用您提供的数据检查下面的代码片段,它将根据多列返回过滤数据。

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

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

Please check below link which can used with just small changes Javascript filter array multiple values – example请检查下面的链接,该链接只需稍作改动即可使用Javascript 过滤器数组多个值 - 示例

A question I was in the middle of answering got (properly) closed as duplicate of this.我正在回答的一个问题被(正确地)关闭为这个问题的副本。 But I don't see any of the answers above quite like this one.但是我看不到上面的任何答案都像这个答案。 So here's one more option.所以这里还有一个选择。

We can write a simple function that takes a specification such as {name: 'mike', house: 'blue'} , and returns a function that will test if the value passed to it matches all the properties.我们可以编写一个简单的函数,它接受诸如{name: 'mike', house: 'blue'}类的规范,并返回一个函数,该函数将测试传递给它的值是否与所有属性匹配。 It could be used like this:它可以这样使用:

 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}

And if we wanted to wrap the filtering into a single function, we could reuse that same function, wrapped up like this:如果我们想将过滤封装到单个函数中,我们可以重用相同的函数,封装如下:

 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}

(Of course that last doesn't have to be curried. We could change that so that we could call it with two parameters at once. I find this more flexible, but YMMV.) (当然最后一个不一定是柯里化的。我们可以改变它,这样我们就可以一次用两个参数调用它。我觉得这更灵活,但是 YMMV。)

Keeping it as a separate function has the advantage that we could then reuse it, in say, a find or some other matching situation.将它作为一个单独的函数保存的好处是我们可以重用它,例如find或其他匹配情况。


This design is very similar to the use of where in Ramda (disclaimer: I'm one of Ramda's authors.) Ramda offers the additional flexibility of allowing arbitrary predicates instead of values that have to be equal.这种设计与在Ramda中使用where非常相似(免责声明:我是 Ramda 的作者之一。)Ramda 提供了额外的灵活性,允许使用任意谓词而不是必须相等的值。 So in Ramda, you might write something like this instead:所以在 Ramda 中,你可能会这样写:

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

It's much the same idea, only a bit more flexible.它的想法大致相同,只是更灵活一点。

We can use different operators to provide multiple condtion to filter the array in the following way我们可以使用不同的运算符来提供多个条件,以如下方式过滤数组

Useing OR (||) Operator:使用 OR (||) 运算符:

 const orFilter = [{a:1, b: 3}, {a:1,b:2}, {a: 2, b:2}].filter(d => (da== 1 || db,== 2)) console.log(orFilter, 'orFilter')

Using AND (&&) Operator:使用 AND (&&) 运算符:

 const andFilter = [{a:1, b: 3}, {a:1,b:2}, {a: 2, b:2}].filter(d => (da== 1 && db,== 2)) console.log(andFilter, 'andFilter')

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"
}]
 */
arr.filter((item) => {
       if(condition)
       {
         return false;
       }
       return true;
    });

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM