简体   繁体   English

JavaScript:创建一个函数来拒绝被认为是真实的数组元素,麻烦数组对象中的值

[英]JavaScript: Creating a function to reject array elements found to be truthy, trouble with values in objects in the array

I'm working on a code challenge assignment. 我正在从事代码挑战任务。 Create a function, reject , that takes an array and a callback function, and removes from the array any items that are found truthy when the callback function is run against them. 创建一个函数, reject ,接受一个数组和一个回调函数,并删除所发现的任何truthy项目时的回调函数对他们运行阵列。 I've written the following: 我写了以下内容:

function reject(collection, callback) {

    for (var i = 0; i < collection.length; i++) {
        if(callback(collection[i]) === true){
            collection.splice(i, 1);
        }
    }

    return collection;

}

and where I'm hitting a wall is a test with an array of key-value pairs. 而我碰壁的地方就是对一组键值对的测试。 The failing test: 测试失败:

var obj = {a:1, b:2, c:3, d:4};
var isOdd = function(value, key, collection) { return value % 2 !== 0; };
var evens = reject(obj, isOdd);
expect(evens).to.eql({b:2, d:4});

Lack of experience has exhausted my ability to search for answers effectively, so here we are. 缺乏经验耗尽了我有效搜索答案的能力,所以我们到了。 Any help or guidance is appreciated. 任何帮助或指导表示赞赏。

Edited to add: Misread the tests in the original instructions (then failed to catch it when copy/pasting the test). 编辑添加:误读了原始说明中的测试(然后在复制/粘贴测试时无法捕获它)。 I definitely know the difference between an object and an array, just thought I saw [{a:1, b:2, c:3, d:4}] in the document but it was actually ({a:1, b:2, c:3, d:4}) for whatever reason. 我绝对知道对象和数组之间的区别,只是以为我在文档中看到了[{a:1,b:2,c:3,d:4}],但实际上是({a:1,b: 2,c:3,d:4})出于任何原因。 Sorry. 抱歉。

You have the right idea, however you need to look at the difference between a JavaScript object and array. 您有正确的想法,但是您需要查看JavaScript对象和数组之间的区别。 Read this and learn the difference . 阅读并学习其中的区别

A JavaScript object does not have the property of .length to return the size of the collection. JavaScript对象不具有.length属性以返回集合的大小。 Use the following loop instead: 请使用以下循环:

for (var key in collection)

A collection does not have the property .splice, that is for arrays. 集合不具有用于数组的属性.splice。 Instead of using .splice to remove the item use 而不是使用.splice删除项目使用

delete collection[key]

Finally, pass the item in the collection to the callback 最后,将集合中的项目传递给回调

callback(collection[key])

Updated Answer: 更新的答案:

function reject(collection, callback) {
    for (var key in collection) {
        if(callback(collection[key]) === true) {
            delete collection[key];
        }
    }
    return collection;
}

var obj = {a:1, b:2, c:3, d:4};     // Use for an object passed
var obj2 = [1, 2, 3, 4];            // Use as an array passed
var isOdd = function(value) { return value % 2 !== 0; };
var evens = reject(obj, isOdd);
console.log(evens);
// expect(evens).to.eql({b:2, d:4});

I think this is what you're trying to do? 我认为这就是您要执行的操作?

function reject(collection, callback) {

    Object.keys(collection).forEach(function(key){
        if(callback(collection[key], key, collection)){
            delete collection[key];
        }
    });

    return collection;
}

var obj = {a:1, b:2, c:3, d:4};
var isOdd = function(value, key, collection) { return value % 2 !== 0; };
var evens = reject(obj, isOdd);
console.log(evens); //prints { b: 2, d: 4 }

Here's the solution I ended up with. 这是我最终得到的解决方案。 To clarify, the tests were passing in arrays and objects, so that's why I first had trouble (with the objects) and then there was some confusion in in the answers. 为了澄清,测试是通过数组和对象传递的,所以这就是为什么我首先遇到麻烦(使用对象),然后答案中有些混乱的原因。 I wrote: 我写:

function reject(collection, callback) {
    if(Array.isArray(collection)){
        for (var i = 0; i < collection.length; i++) {
            if(callback(collection[i]) === true){
                collection.splice(i, 1);
            }
        }
    } else {
        for (number in collection){
            if(callback(collection[number]) === true){
                delete collection[number];
            }
        }
    };

    return collection;
}

I know it could likely be much cleaner, but just for the sake of clarity, I wanted to show a solution that works. 我知道它可能会更清洁,但是为了清楚起见,我想展示一个可行的解决方案。

I have created a not() function, which accepts a function and can be passed to a filter function: 我创建了一个not()函数,该函数接受一个函数并可以传递给过滤器函数:

// wraps passed function, so that when called,
// the return value will be negated
function not(a){
    return function(){
        return !a.apply(a, arguments);
    }
}

Usage: 用法:

// some sample data
var animals = [
    {name: 'Pete', type: 'fish'},
    {name: 'Jim', type: 'fish'},
    {name: 'Tom', type: 'cat'}
];

// a simple filter callback
var isCat = function(animal){
    return animal.type === 'cat';
};

// gather all animals, which are not cats
var fishes = animals.filter(not(isCat));

I know it could likely be much cleaner 我知道这可能会更清洁

Just for the fun, here is how I alter the answer step by step to make it a bit cleaner. 只是为了好玩,这是我一步一步地更改答案以使其更简洁的方法。

Making it immutable 使其不变

Changing the collection will change the original object, which is passed to the function, since arrays and objects are reference types. 更改集合将更改原始对象,该对象将传递给函数,因为数组和对象是引用类型。 To solve this, you can clone the collection and work on that, or you can copy the elements, which are not rejected by the callback. 要解决此问题,您可以克隆集合并对其进行处理,也可以复制未被回调拒绝的元素。 I'm doing the latter. 我正在做后者。

function reject(collection, callback) {
    var ret;

    if(Array.isArray(collection)){
        ret = [];
        for (var i = 0; i < collection.length; i++) {
            if(!callback(collection[i])){
                ret.push(collection[i]);
            }
        }
    } else {
        ret = {};
        for (number in collection){
            if(!callback(collection[number])){
                ret[number] = collection[number];
            }
        }
    }

    return ret;
}

Shortening with ES5 用ES5缩短

The loops' mechanics and the actual code done by the loop is entangled, we can have a much cleaner code, if we stop concentrating on how to write a loop and let JS do it. 如果我们不再专注于如何编写循环并让JS来完成,那么循环的机制和由循环完成的实际代码就会纠缠在一起,我们可以得到更加整洁的代码。 For example: notice how I refer to the individual elements of the array collection as value , instead of collection[i] . 例如:注意我如何将数组集合的各个元素称为value ,而不是collection [i]

function reject(collection, callback) {
    var ret;

    if(Array.isArray(collection)){
        ret = [];
        collection.forEach(function(value){
            if(!callback(value)){
                ret.push(value);
            }
        });
    } else {
        ret = {};
        Object.keys(collection).forEach(function(key){
            var value = collection[key];
            if(!callback(value)){
                ret[key] = value;
            }
        });
    }

    return ret;
}

Changing if to filter() 更改是否要filter()

Array.prototype.filter() is a bit more useful for us, than forEach, since in the core of the loop you can simply return a truthy or falsy value and filter will handle collecting the data to a new array based on that automatically for you. Array.prototype.filter()对我们来说比forEach有用一点,因为在循环的核心中,您可以简单地返回一个true或falsy值,并且filter会根据该值自动将数据收集到一个新数组中。您。

function reject(collection, callback) {
    var ret;

    if(Array.isArray(collection)){
        ret = collection.filter(function(value){
            return !callback(value);
        });
    } else {
        ret = {};
        Object.keys(collection).filter(function(key){
            return !callback(collection[key]);
        }).forEach(function(key){
            ret[key] = collection[key];
        });
    }

    return ret;
}

Using reduce for objects 对对象使用reduce

The goal would be to minimize functions, which go outside from their scope in order to work correctly. 目的是使功能超出其范围,以使其正常工作,而将其最小化。 In the objects part we can use Array.prototype.reduce() instead of forEach() and simply return it's output directly to the ret value, when we are done, just as we did in the Array part with filter(). 在对象部分,我们可以使用Array.prototype.reduce()代替forEach(),并在完成后直接将其输出直接返回到ret值,就像在Array部分中使用filter()一样。

function reject(collection, callback) {
    var ret;

    if(Array.isArray(collection)){
        ret = collection.filter(function(value){
            return !callback(value);
        });
    } else {
        ret = Object.keys(collection).filter(function(key){
            return !callback(collection[key]);
        }).reduce(function(obj, key){
            obj[key] = collection[key];
            return obj;
        }, {});
    }

    return ret;
}

Shortening functions with ES6 使用ES6缩短功能

Since we are already using Array.isArray(), which is an ES6 method, we can try using arrow functions to compress anonymus functions. 由于我们已经在使用Array.isArray(),这是一种ES6方法,因此我们可以尝试使用箭头函数来压缩匿名函数。

function reject(collection, callback) {
    var ret;

    if(Array.isArray(collection)){
        ret = collection.filter(value => !callback(value));
    } else {
        ret = Object.keys(collection)
            .filter(key => !callback(collection[key]))
            .reduce((obj, key) => {
                obj[key] = collection[key];
                return obj;
            }, {})
        ;
    }

    return ret;
}

We don't need the ret variable 我们不需要ret变量

We previously removed the need to access the ret value in our logics, we can use a ternary operator to directly return the value generated by the expressions. 之前我们取消了在逻辑中访问ret值的需要,我们可以使用三元运算符直接返回由表达式生成的值。

function reject(collection, callback) {
    return (
        Array.isArray(collection)
        ? collection
            .filter(value => !callback(value))
        : Object.keys(collection)
            .filter(key => !callback(collection[key]))
            .reduce((obj, key) => {
                obj[key] = collection[key];
                return obj;
            }, {})
    )
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM