Разделение массива на функцию фильтра
У меня есть массив Javascript, который я хотел бы разбить на два, исходя из того, возвращает ли функция, вызываемая каждым элементом, true
или false
. По сути, это array.filter
, но я также хотел бы иметь под рукой элементы, которые были отфильтрованы.
В настоящее время я планирую использовать array.forEach
и вызывать функцию предиката для каждого элемента. В зависимости от того, является ли это true или false, я нажимаю текущий элемент на один из двух новых массивов. Есть ли более элегантный или лучший способ сделать это? array.filter
, где будет нажимать элемент на другой массив, прежде чем он вернет false
, например?
Ответы
Ответ 1
С ES6 вы можете использовать синтаксис распространения с уменьшением:
function partition(array, isValid) {
return array.reduce(([pass, fail], elem) => {
return isValid(elem) ? [[...pass, elem], fail] : [pass, [...fail, elem]];
}, [[], []]);
}
const [pass, fail] = partition(myArray, (e) => e > 5);
Или в одной строке:
const [pass, fail] = a.reduce(([p, f], e) => (e > 5 ? [[...p, e], f] : [p, [...f, e]]), [[], []]);
Ответ 2
Вы можете использовать lodash.partition
var users = [
{ 'user': 'barney', 'age': 36, 'active': false },
{ 'user': 'fred', 'age': 40, 'active': true },
{ 'user': 'pebbles', 'age': 1, 'active': false }
];
_.partition(users, function(o) { return o.active; });
// → objects for [['fred'], ['barney', 'pebbles']]
// The `_.matches` iteratee shorthand.
_.partition(users, { 'age': 1, 'active': false });
// → objects for [['pebbles'], ['barney', 'fred']]
// The `_.matchesProperty` iteratee shorthand.
_.partition(users, ['active', false]);
// → objects for [['barney', 'pebbles'], ['fred']]
// The `_.property` iteratee shorthand.
_.partition(users, 'active');
// → objects for [['fred'], ['barney', 'pebbles']]
или ramda.partition
R.partition(R.contains('s'), ['sss', 'ttt', 'foo', 'bars']);
// => [ [ 'sss', 'bars' ], [ 'ttt', 'foo' ] ]
R.partition(R.contains('s'), { a: 'sss', b: 'ttt', foo: 'bars' });
// => [ { a: 'sss', foo: 'bars' }, { b: 'ttt' } ]
Ответ 3
Звучит очень похоже на метод Ruby Enumerable#partition
.
Если у функции не могут быть побочные эффекты (т.е. она не может изменить исходный массив), тогда нет более эффективного способа разбиения массива, чем повторение каждого элемента, и нажатие элемента на один из ваших двух массивы.
Говоря это, возможно, более "элегантно" создать метод на Array
для выполнения этой функции. В этом примере функция фильтра выполняется в контексте исходного массива (т.е. this
будет исходным массивом), и он получает элемент и индекс элемента в качестве аргументов (аналогично jQuery each
метод):
Array.prototype.partition = function (f){
var matched = [],
unmatched = [],
i = 0,
j = this.length;
for (; i < j; i++){
(f.call(this, this[i], i) ? matched : unmatched).push(this[i]);
}
return [matched, unmatched];
};
console.log([1, 2, 3, 4, 5].partition(function (n, i){
return n % 2 == 0;
}));
//=> [ [ 2, 4 ], [ 1, 3, 5 ] ]
Ответ 4
В функции фильтра вы можете подтолкнуть свои ложные элементы к другой переменной внешней функции:
var bad = [], good = [1,2,3,4,5];
good = good.filter(function (value) { if (value === false) { bad.push(value) } else { return true});
Конечно, value === false
должно быть реальным сравнением;)
Но он выполняет почти ту же операцию, что и forEach
. Я думаю, вы должны использовать forEach
для лучшей читаемости кода.
Ответ 5
Вы можете использовать уменьшить для этого:
function partition(array, callback){
return array.reduce(function(result, element, i) {
callback(element, i, array)
? result[0].push(element)
: result[1].push(element);
return result;
}, [[],[]]
);
};
Обновить. Используя синтаксис ES6, вы также можете сделать это с помощью рекурсии:
function partition([current, ...tail], f, [left, right] = [[], []]) {
if(current === undefined) {
return [left, right];
}
if(f(current)) {
return partition(tail, f, [[...left, current], right]);
}
return partition(tail, f, [left, [...right, current]]);
}
Ответ 6
Попробуйте следующее:
function filter(a, fun) {
var ret = { good: [], bad: [] };
for (var i = 0; i < a.length; i++)
if (fun(a[i])
ret.good.push(a[i]);
else
ret.bad.push(a[i]);
return ret;
}
Ответ 7
Я придумал этого маленького парня. Он использует для всех и каждого, что вы описали, но, на мой взгляд, выглядит чисто и красноречиво.
//Partition function
function partition(array, filter) {
let pass = [], fail = [];
array.forEach((e, idx, arr) => (filter(e, idx, arr) ? pass : fail).push(e));
return [pass, fail];
}
//Run it with some dummy data and filter
const [lessThan5, greaterThanEqual5] = partition([0,1,4,3,5,7,9,2,4,6,8,9,0,1,2,4,6], e => e < 5);
//Output
console.log(lessThan5);
console.log(greaterThanEqual5);
Ответ 8
Легко читать.
const partition = (arr, condition) => {
const trues = arr.filter(el => condition(el));
const falses = arr.filter(el => !condition(el));
return [trues, falses];
};
// sample usage
const nums = [1,2,3,4,5,6,7]
const [evens, odds] = partition(nums, (el) => el%2 == 0)
Ответ 9
Я закончил тем, что сделал это, потому что это легко понять (и полностью напечатано с помощью машинописи).
const partition = <T>(array: T[], isValid: (element: T) => boolean): [T[], T[]] => {
const pass: T[] = []
const fail: T[] = []
array.forEach(element => {
if (isValid(element)) {
pass.push(element)
} else {
fail.push(element)
}
})
return [pass, fail]
}
// usage
const [pass, fail] = partition([1, 2, 3, 4, 5], (element: number) => element > 3)