Подтвердить что ты не робот

Разделение массива на функцию фильтра

У меня есть массив Javascript, который я хотел бы разбить на два, исходя из того, возвращает ли функция, вызываемая каждым элементом, true или false. По сути, это array.filter, но я также хотел бы иметь под рукой элементы, которые были отфильтрованы.

В настоящее время я планирую использовать array.forEach и вызывать функцию предиката для каждого элемента. В зависимости от того, является ли это true или false, я нажимаю текущий элемент на один из двух новых массивов. Есть ли более элегантный или лучший способ сделать это? array.filter, где будет нажимать элемент на другой массив, прежде чем он вернет false, например?

4b9b3361

Ответ 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;
}

DEMO

Ответ 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)