У меня есть массив Javascript, который я хотел бы разделить на два в зависимости от того, возвращает ли функция, вызываемая для каждого элемента, true
или false
. По сути, это array.filter
, но я хотел бы также иметь под рукой те элементы , которые были отфильтрованы из .
В настоящее время я планирую использовать array.forEach
и вызывать функцию предиката для каждого элемента. В зависимости от того, истинно это или ложно, я помещаю текущий элемент в один из двух новых массивов. Есть ли более элегантный или лучший способ сделать это? array.filter
Где будет толкать элемент на другой массив , прежде чем он возвращается false
, например?
javascript
Майк Чен
источник
источник
.filter
но такие побочные эффекты трудно отследить и понять. Просто перебирайте массив и переходите к одному или другому массиву.Ответы:
С ES6 вы можете использовать синтаксис распространения с помощью reduce:
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]]), [[], []]);
источник
Вы можете использовать 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' } ]
источник
Для этого можно использовать сокращение:
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]]); }
источник
[...left, current]
или[...right, current]
- для каждого элемента. Я не знаю точного внутреннего устройства, но уверен, что построение обходится дороже, чем просто вставка элемента в массив. Кроме того, как правило, рекурсия дороже, чем итерация , поскольку она включает в себя каждый раз создание «кадра стека».Это очень похоже на метод Руби
Enumerable#partition
.Если функция не может иметь побочных эффектов (т.е. она не может изменить исходный массив), тогда нет более эффективного способа разбить массив, чем итерация по каждому элементу и перемещение элемента в один из двух массивов.
При этом, возможно, более «элегантно» создать метод
Array
для выполнения этой функции. В этом примере функция фильтра выполняется в контексте исходного массива (т.е.this
будет исходным массивом), и она получает элемент и индекс элемента в качестве аргументов (аналогично методу jQueryeach
):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 ] ]
источник
Я придумал этого маленького парня. Он использует для всех без исключения то, что вы описали, но, на мой взгляд, он выглядит чистым и лаконичным.
//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);
источник
for
цикл, как в старом ответе @qwertymk. Например, для массива, содержащего 100 000 элементов, в моей системе он был в два раза медленнее.В функции фильтра вы можете поместить свои ложные элементы в другую переменную вне функции:
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
для лучшей читаемости кода.источник
Легко читать.
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)
источник
Попробуй это:
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; }
ДЕМО
источник
Как насчет этого?
[1,4,3,5,3,2].reduce( (s, x) => { s[ x > 3 ].push(x); return s;} , {true: [], false:[]} )
Возможно, это более эффективно, чем оператор спреда
Или чуть короче, но уродливее
[1,4,3,5,3,2].reduce( (s, x) => s[ x > 3 ].push(x)?s:s , {true: [], false:[]} )
источник
Многие ответы здесь используют
Array.prototype.reduce
для создания изменяемого аккумулятора и справедливо указывают, что для больших массивов это более эффективно, чем, скажем, использование оператора распространения для копирования нового массива на каждой итерации. Обратной стороной является то, что это не так красиво, как «чистое» выражение с использованием короткого лямбда-синтаксиса.Но способ обойти это - использовать оператор запятой. В языках типа C запятая - это оператор, который всегда возвращает правый операнд. Вы можете использовать это для создания выражения, которое вызывает функцию void и возвращает значение.
function partition(array, predicate) { return array.reduce((acc, item) => predicate(item) ? (acc[0].push(item), acc) : (acc[1].push(item), acc), [[], []]); }
Если вы воспользуетесь преимуществом того факта, что логическое выражение неявно преобразуется в число как 0 и 1, и вы можете сделать его еще более кратким, хотя я не думаю, что это так удобно для чтения:
function partition(array, predicate) { return array.reduce((acc, item) => (acc[+!predicate(item)].push(item), acc), [[], []]); }
Применение:
const [trues, falses] = partition(['aardvark', 'cat', 'apple'], i => i.startsWith('a')); console.log(trues); // ['aardvark', 'apple'] console.log(falses); // ['cat']
источник
ОДНОСЛОЙНАЯ перегородка
const partition = (a,f)=>a.reduce((p,q)=>(p[+!f(q)].push(q),p),[[],[]]);
ДЕМО
// to make it consistent to filter pass index and array as arguments const partition = (a, f) => a.reduce((p, q, i, ar) => (p[+!f(q, i, ar)].push(q), p), [[], []]); console.log(partition([1, 2, 3, 4, 5], x => x % 2 === 0)); console.log(partition([..."ABCD"], (x, i) => i % 2 === 0));
Для машинописного текста
const partition = <T>( a: T[], f: (v: T, i?: number, ar?: T[]) => boolean ): [T[], T[]] => a.reduce((p, q, i, ar) => (p[+!f(q, i, ar)].push(q), p), [[], []]);
источник
В итоге я сделал это, потому что это легко понять (и полностью набрано машинописным текстом).
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)
источник