【问题标题】:Javascript: place elements that dont match filter predicate into seperate arrayJavascript:将不匹配过滤谓词的元素放入单独的数组中
【发布时间】:2015-03-19 17:38:45
【问题描述】:

这可能比我想象的要简单得多,但我一直在尝试 javascript 中的 .map() 和 .filter() 函数。我想要做的是使用 .filter() 创建一个数组,并为与第一个过滤器的谓词不匹配的元素创建另一个数组。到目前为止我所拥有的:

function test(array, predicate){
    var filterTrue = array.filter(predicate);
    var filterFalse = ??
    // rest of method
}

有没有办法将与谓词不匹配的项目转储到 filterFalse 中?可能不言而喻,但谓词通常是某种函数

编辑:顺便说一句,我试过了:

var filterFalse = array.filter(!predicate);

但由于我仍在努力理解的原因,这似乎不起作用(对此的任何帮助也将不胜感激)

【问题讨论】:

  • 将新数组与原始数组进行比较以找到不匹配的项目。google.nl/…
  • 你看过Underscore.js 的partition 函数吗(见underscorejs.org/#partition)?就我的理解而言,它完全符合您的需求。
  • @PermaFrost 实际上我没有,诚然我没有看过 underscore.js。虽然我想将解决方案保留为纯 javascript,但我会花一些时间阅读下划线(我更多的是服务器端开发人员,这些天我在前端框架等方面非常落后)

标签: javascript arrays


【解决方案1】:

在这种情况下,您最好使用forEach,但我会解决您的问题,即为什么!predicate 不起作用(以及如何制作类似的东西)下面也是。

一、简单的forEach解决方案:

普罗西克:

function test(array, predicate){
    var filterTrue = [];
    var filterFalse = [];
    array.forEach(function(value) {
        if (predicate(value)) {
            filterTrue.push(value);
        } else {
            filterFalse.push(value);
        }
    });
    // rest of method
}

更简洁一点:

function test(array, predicate){
    var filterTrue = [];
    var filterFalse = [];
    array.forEach(function(value) {
        (predicate(value) ? filterTrue : filterFalse).push(value);
    });
    // rest of method
}

顺便说一句,我试过了:

var filterFalse = array.filter(!predicate);

但这似乎不起作用,原因我仍在努力理解

必须是:

var filterFalse = array.filter(function(entry) {
    return !predicate(entry);
});

...确实可行,但这意味着您要通过数组两次并为每个元素调用谓词两次。这就是我推荐forEach 的原因:只需要遍历数组一次,并且每个条目只调用一次谓词。

您的var filterFalse = array.filter(!predicate); 不起作用的原因是它采用了包含对函数的引用的predicate 变量,并在逻辑上将其与! 反转。非空对象引用(函数是对象)的逻辑反转版本是false,因此您实际上是将false 传递给filter

更完整地说:一元 ! 将其操作数强制转换为布尔值,然后返回它的相反值(false 用于 truetrue 用于 false)。所以!predicate 将导致false 的任何predicate 值强制转换为true(也称为“真实”值),并将导致true 任何predicate 值强制转换为@987654348 @(又名“虚假”值)。那么什么是“真”和“假”值呢? “假”值为0""nullundefinedNaN,当然还有false; “真实”值是所有其他值,包括所有非null 对象引用。

如果您经常使用谓词进行编程,并且想要一种方式来表示“非谓词”并获得一个可以为您提供反转结果的函数,您可以这样做:

function not(predicate) {
    return function() {
        return !predicate.apply(this, arguments);
    };
}

然后:

var filterFalse = array.filter(not(predicate));

not 函数的工作原理是这样的:它返回一个新函数,当调用该函数时,它将调用您提供给它的谓词函数,并传递调用它时使用的 this 值和调用它的所有参数使用(通过Function#apply——spec | MDN),逻辑反转它从谓词获得的返回值,然后返回该反转值。

但同样,使用它需要两次遍历数组。不过,有时使用高级抽象,这可能比 forEach 解决方案更可取。

最后,如果你经常做这个“非此即彼”的事情,你当然可以创建一个函数来做那个

function divvyUp(array, predicate) {
    var filterTrue = [], filterFalse = [];
    array.forEach(function(value) {
        (predicate(value) ? filterTrue : filterFalse).push(value);
    });
    return {
        filterTrue: filterTrue,
        filterFalse: filterFalse
    };
}

【讨论】:

  • 我没想到会这么详细,非常详细,谢谢
  • @jbailie1991: :-) 有时一个问题会抓住我。你的,谢谢。
【解决方案2】:

编辑

下面是 lodash 在纯 JavaScript 中的 partition 方法的实现,在 JSDoc 中带有 TypeScript 类型。它使用Array.prototype.reduce。正如 JSDoc 注释所说,此分区函数执行以下操作:

返回一个数组,索引处有两个数组 0 和 1。索引 0 处的数组是所有项目 在arr 中通过了predicate 真值测试 返回一个真实的值。索引 1 处的数组是所有项目 在arr 中,通过返回未通过predicate 真值测试 一个虚假的值。

// ----- partition function declaration -----
/** Returns an array with two arrays at index
 * 0 and 1. The array at index 0 is all the items
 * in `arr` that passed the `predicate` truth test by
 * returning a truthy value. The array at index 1 is all the items
 * in `arr` that failed the `predicate` truth test by returning
 * a falsy value.
 * @template {any} T
 * @param {Array<T>} arr
 * @param {(el:T, index:number, arr:Array<T>) => any} predicate
 * @returns {[Array<T>, Array<T>]}
 */
function partition(arr, predicate) {
  return arr.reduce(
    // this callback will be called for each element of arr
    function(partitionsAccumulator, arrElement, i, arr) {
      if (predicate(arrElement, i, arr)) {
        // predicate passed push to left array
        partitionsAccumulator[0].push(arrElement);
      } else {
        // predicate failed push to right array
        partitionsAccumulator[1].push(arrElement);
      }
      // whatever is returned from reduce will become the new value of the
      // first parameter of the reduce callback in this case 
      // partitionsAccumulator variable if there are no more elements
      // this return value will be the return value of the full reduce
      // function.
      return partitionsAccumulator;
    },
    // the initial value of partitionsAccumulator in the callback function above
    // if the arr is empty this will be the return value of the reduce
    [[], []]
  );
}


// ----- function usage examples -----
// This partition gets all numbers which are even in the
// first array (all these numbers returned true for the predicate)
// and returns all numbers which are odd in the second array
var res = partition([1, 2, 3], function(number) {
  return number % 2 === 0;
});
console.log(res); // → [[2], [1, 3]]
// This partition gets all indexes that are more than half
// way through the array.
res = partition([1, 2, 3, 4], function(number, index, array) {
  return index > Math.floor(array.length / 2) - 1;
});
console.log(res); // → [[3, 4], [1, 2]]
// This partition gets all strings with length greater than 4
res = partition(["bam!", "kazaam!", "blam!", "wam!", "jam!"], (string) => {
  return string.length > 4;
});
console.log(res); // → [["kazaam!", "blam!"], ["bam!", "wam!", "jam!"]]

使用 JSDoc 类型的好处是,如果您有像 VSCode 这样的编辑器,当您在 mac 上按 command 或在 windows 上按 ctrl 时,它会向您显示类型和描述。看起来像这样:

鉴于我在 JSDoc 注释中使用了模板 T,VSCode 足够聪明,因为这张图片中参数 1 中的数组充满了数字,所以 T 是一个数字。如果您传入一个字符串数组,它将正确提示谓词的el 参数的类型以及内部数组的返回项值作为字符串,因为它们也使用模板T。


原答案

如果您还没有使用库,讨厌启动它们,但 lodash 有一个功能正是这样做的,称为 partition

_.partition([1, 2, 3], function(n) {
  return n % 2;
});
// → [[1, 3], [2]]

_.partition([1.2, 2.3, 3.4], function(n) {
  return this.floor(n) % 2;
}, Math);
// → [[1.2, 3.4], [2.3]]

创建一个元素数组,分成两组,第一组 包含元素谓词返回truthy for,而第二个 其中包含元素谓词返回错误。谓词是 绑定到 thisArg 并使用三个参数调用:(value, index|key, 收藏)。

如果为谓词提供了属性名称,则创建的 _.property style 回调返回给定元素的属性值。

如果还为 thisArg 提供了值,则创建的 _.matchesProperty 对于具有匹配属性的元素,样式回调返回 true 值,否则为假。

如果为谓词提供了一个对象,则创建的 _.matches 样式 对于具有以下属性的元素,回调返回 true 给定对象,否则为假。参数

  1. collection (Array|Object|string):要迭代的集合。
  2. [predicate=_.identity] (Function|Object|string):每次迭代调用的函数。
  3. [thisArg] (*):谓词的 this 绑定。

返回

(Array):返回分组元素的数组。

更多示例

var users = [
  { 'user': 'barney',  'age': 36, 'active': false },
  { 'user': 'fred',    'age': 40, 'active': true },
  { 'user': 'pebbles', 'age': 1,  'active': false }
];

var mapper = function(array) {
  return _.pluck(array, 'user');
};

// using the `_.matches` callback shorthand
_.map(_.partition(users, { 'age': 1, 'active': false }), mapper);
// → [['pebbles'], ['barney', 'fred']]

// using the `_.matchesProperty` callback shorthand
_.map(_.partition(users, 'active', false), mapper);
// → [['barney', 'pebbles'], ['fred']]

// using the `_.property` callback shorthand
_.map(_.partition(users, 'active'), mapper);
// → [['fred'], ['barney', 'pebbles']]

【讨论】:

    【解决方案3】:

    如果你想要被拒绝的值,那么让过滤器接受一个返回谓词逆的函数:

    function test(array, predicate) {
        var filterTrue = array.filter(predicate);
        var filterFalse = array.filter(not(predicate));
       
       log(filterTrue, filterFalse);
    }
    
    function not(fn) {
        return function () {
            return !fn.apply(this, arguments);
        }
    }
    
    function log(filterTrue, filterFalse) {
         document.write('<pre> predicate true:' + JSON.stringify(filterTrue) + '</pre>');
         document.write('<pre> predicate false: ' + JSON.stringify(filterFalse) + '</pre>');
    }
    
    var array = [1, 2, 3, 4, 5];
    
    function isEven(value) {
        return value % 2 === 0;
    }
    
    test(array, isEven);

    【讨论】:

    • 我喜欢这个答案,它简洁明了,并且设法对两个数组都使用了过滤器,但是由于各种解决方案和解释中包含大量细节,因此接受了另一个答案。辛苦了
    • @T.J.Crowder 我什至没有因为窃取我的答案而生气 ;)
    • @Moogs: :-) 显然我在写not 时没有看到它。只是收敛。
    【解决方案4】:

    function test(array, predicate){
        var filterTrue = array.filter(predicate);
        var filterFalse = array.filter(function(data) { return !predicate(data); })
        return { filterTrue: filterTrue, filterFalse: filterFalse }
    }
    
    mya = [ { id:1, desc: "one" }, {  id:2, desc: "two"  }, {  id:3, desc: "three" }];
    
    console.info(test(mya, function(data) { return data.id == 1; }))

    反转谓词。

    【讨论】:

    • 抱歉发帖有点慢。没有看到相同的答案。
    猜你喜欢
    • 1970-01-01
    • 2020-06-22
    • 1970-01-01
    • 2015-03-09
    • 1970-01-01
    • 2020-04-29
    • 2018-02-05
    • 2011-01-15
    • 1970-01-01
    相关资源
    最近更新 更多