Javascript 过滤器数组多个条件

我想简化一个对象数组,假设我有下面的数组:

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时,只有最后一个用户应该被添加到数组结果中,但是我最终得到了数组中的两个元素。

我有一个小的想法,为什么会发生这种情况,但仍然坚持它,无法找到一个好的解决方案来修复它。任何帮助都是值得的。谢谢。

489620 次浏览

你可以这样做

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)

如果代码的最终目的是获得经过过滤的用户,那么我将反转 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;
}

我觉得这个可能会有帮助。

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

对于那些喜欢简洁代码的人来说,这是另一个选择。

注意 : 过滤器方法可以接受额外的 这个参数,然后使用 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);
<div id="result"></div>

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

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

也可以这样做:

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

下面是 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)

功能性溶液

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

由一些小帮手组成:

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

使用 Array. Filter ()箭头函数,我们可以使用

Users = users.filter (x = > x.name = = ‘ Mark’& & x.address = = ‘ England’) ;

下面是完整的代码片段

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

用夸张的说法,

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

在第六季,

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

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

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

这是我想出的另一个方法,其中 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)


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

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

如果要在 filter中放置多个条件,可以使用 &&||操作符。

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

一个干净和实用的解决方案

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

使用 loash 而不是纯 javascript

这实际上是非常简单的使用 loash 和非常容易添加/修改过滤器。

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. 省略函数检查您的 过滤器对象并删除任何为 null 或未定义的值(如果取出该值,Lodash.filter函数将不返回任何结果)。

  2. 过滤器函数将过滤掉所有值与作为第二个参数传递给函数的对象(在本例中是 过滤器对象)不匹配的对象

为什么要用这个?

假设你有这个物体:

const myFiltersObj = {


name: "Java",
age: 50


};

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

const myFiltersObj = {


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


};

调用 GetUsersWithFilter函数,它就可以正常工作了。如果跳过,比如说对象中的 name 属性,那么 GetUsersWithFilter函数将根据电子邮件和国家进行过滤。

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

你想要多少过滤器都可以。

arr.filter((item) => {
if(condition)
{
return false;
}
return true;
});

我的解决方案,基于 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)
});
});

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

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

请检查下面的代码片段与您提供的数据,它将返回基于多个列的筛选数据。

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 过滤器数组多值-示例

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

我们可以编写一个简单的函数,该函数接受诸如 {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}

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

Keeping it as a separate function has the advantage that we could then reuse it, in say, a find or some other matching situation.


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. So in Ramda, you might write something like this instead:

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

这是差不多的想法,只是更灵活一点。

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

对于 es6要好得多。或者您可以像这样使用 | | (或)运算符

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