JavaScript: создание функции для отклонения элементов массива, признанных истинными, проблемы со значениями объектов в массиве

Я работаю над заданием вызова кода. Создайте rejectфункцию, которая принимает массив и функцию обратного вызова и удаляет из массива все элементы, найденные истинными при выполнении функции обратного вызова для них. Я написал следующее:

function reject(collection, callback) {

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

    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);
expect(evens).to.eql({b:2, d:4});

Отсутствие опыта исчерпало мою способность эффективно искать ответы, поэтому мы здесь. Любая помощь или руководство приветствуются.

Отредактировано для добавления:
Неправильно истолковать тесты в исходных инструкциях (затем не удалось поймать его при копировании/вставке теста). Я определенно знаю разницу между объектом и массивом, просто думал, что видел [{a:1, b:2, c:3, d:4}] в документе, но это было на самом деле ({a:1, b:2, c:3, d:4}) по какой-то причине. Извиняюсь.

5 ответов

  1. У вас есть правильная идея, однако вам нужно посмотреть на разницу между объектом JavaScript и массивом. Прочитайте это и узнайте разницу .

    Объект JavaScript не имеет свойства .длина для возврата размера коллекции. Вместо этого используйте следующий цикл:

    for (var key in collection)
    

    Коллекция не имеет свойство .splice, то есть для массивов. Вместо использования .соединение для удаления использования элемента

    delete collection[key]
    

    Наконец, передайте элемент коллекции обратному вызову

    callback(collection[key])
    

    Обновленный Ответ:

    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});
    
  2. Я думаю, это то, что вы пытаетесь сделать?

    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 }
    
  3. Вот решение, которое я получил. Чтобы прояснить, тесты проходили в массивах и объектах, поэтому у меня сначала были проблемы (с объектами), а затем была некоторая путаница в ответах. Я написал:

    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;
    }
    

    Я знаю, что это может быть намного чище, но просто ради ясности я хотел показать решение, которое работает.

  4. Я создал функцию not (), которая принимает функцию и может быть передана функции фильтра:

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

    Использование:

    // 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));
    
  5. Я знаю, что это может быть намного чище

    Просто для удовольствия, вот как я изменяю ответ шаг за шагом, чтобы сделать его немного чище.

    Сделать его неизменным

    Изменение коллекции изменит исходный объект, который передается функции,
    поскольку массивы и объекты являются ссылочными типами. Чтобы решить эту проблему, можно клонировать коллекцию и работать
    на этом, или вы можете скопировать элементы, которые не отклоняются обратным вызовом. Я делаю последнее.

    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;
    }
    

    Укорочение с ES5

    Механика петель и фактический код, выполненный петлей, запутан, мы можем иметь гораздо более чистый
    код, если мы перестанем концентрироваться на том, как написать цикл, и позволим JS сделать это. Например: обратите внимание, как
    Я ссылаюсь на отдельные элементы коллекции array как 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;
    }
    

    Изменение if на filter()

    Матрица.прототип.filter() немного полезнее для нас, чем forEach, так как в ядре цикла вы можете
    просто верните значение true или false, и фильтр обработает сбор данных в новый массив на основе
    это автоматически для вас.

    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;
    }
    

    Использование reduce для объектов

    Целью было бы минимизировать функции, которые выходят за пределы своей области для правильной работы.
    В части объекты можно использовать массив.прототип.уменьшить () вместо forEach () и просто вернуть
    выведите значение ret, когда мы закончим, так же, как мы сделали в части массива с 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;
    }
    

    Укорочение функций с ES6

    Так как мы уже используем Array.isArray (), который является методом ES6, мы можем попробовать использовать функции со стрелками
    сжатие функций anonymus.

    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;
    }
    

    Нам не нужна переменная ret

    Мы ранее удалили необходимость доступа к значению 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;
                }, {})
        )
    }