подчеркните, как фильтровать все объекты, когда есть несколько параметров - PullRequest
1 голос
/ 16 июня 2020

У меня проблема, как правильно фильтровать данные, когда я передаю одно или несколько значений из фильтров на странице. Например, у меня есть такой объект с данными:

let data = [
    {'name': 'test 1', 'address': 'New York', 'class_id': [1, 2, 3], 'country': 'USA', 'country_id': 20},
    {'name': 'test 2', 'address': 'Lisbona', 'class_id': [2, 3], 'country': 'Portugal', 'country_id': 12},
    {'name': 'test 3', 'address': 'New York', 'class_id': [2], 'country': 'USA', 'country_id': 20},
    {'name': 'test 4', 'address': 'Atlanta', 'class_id': [2], 'country': 'USA', 'country_id': 20},
    {'name': 'test 5', 'address': 'New York', 'class_id': [3], 'country': 'USA', 'country_id': 20},
    {'name': 'test 6', 'address': 'Rio', 'class_id': [1], 'country': 'Brazil', 'country_id': 11},
]

Я хотел бы отфильтровать определенные c данные, когда я передаю, например, одно значение, чтобы оно выглядело в class_id, например: [3]

но я также хотел бы отфильтровать все объекты, которые соответствуют большему количеству условий, т.е. отфильтровать, когда class_id: [3] и country_id: 20

, чтобы результат возвращал следующий результат

data = [
    {'name': 'test 1', 'address': 'New York', 'class_id': [1, 2, 3], 'country': 'USA', 'country_id': 20},
    {'name': 'test 5', 'address': 'New York', 'class_id': [3], 'country': 'USA', 'country_id': 20},
]

Пробовал решения с использованием _.where не помогли. Я также пробовал использовать _.filter и _.contains, он работает, только если я ищу по class_id, но если я задаю второе условие в фильтрах, то есть country_id, к сожалению, тогда ничего не возвращает

1 Ответ

0 голосов
/ 28 августа 2020

Этот вопрос - хорошее упражнение в композиции и абстракции. Давайте рассмотрим некоторые функции, которые может предложить Underscore , и то, как мы можем их использовать. Постепенно мы объединим их в большее целое, пока не достигнем своей цели.

_.contains принимает коллекцию (массив или объект) и значение. Если коллекция содержит значение, оно возвращает true, иначе false.

_.contains([1, 2, 3], 3) // true

_.partial принимает функцию и любое количество аргументов, которые вы можете захотеть » pre-fill "в качестве аргументов этой функции. Затем он возвращает новую функцию с предварительно заполненными аргументами. Вы можете использовать _ в качестве заполнителя для аргументов, которые вы хотите оставить открытыми.

// a version of _.contains that has the second argument pre-filled
// with the value 3
var contains3 = _.partial(_.contains, _, 3);

// the next line is equivalent to calling _.contains([1, 2, 3], 3)
contains3([1, 2, 3]) // true

_.property принимает имя и возвращает функцию. Эта функция принимает объект и возвращает значение свойства с переданным вами именем.

var getName = _.property('name');

getName({'name': 'test 1'}) // 'test 1'

_.compose принимает любое количество функций и возвращает новую функцию. Аргументы, которые вы передаете этой новой функции, передаются самой правой функции, которую вы передали в _.compose. Затем, справа налево, каждая функция вызывается с результатом функции справа. Наконец, новая функция возвращает результат самой левой функции.

// a function that takes an object, reads its class_id property,
// and checks whether that contains the value 3
var classIdContains3 = _.compose(contains3, _.property('class_id'));

classIdContains3({'class_id': [1, 2, 3]}) // true

Мы можем превратить это в функцию, которая будет возвращать такое «свойство содержит средство проверки значения» для любой комбинации имени свойства и значения.

function propertyContains(name, value) {
    return _.compose(
        _.partial(_.contains, _, value),
        _.property(name)
    );
}

var classIdContains3 = propertyContains('class_id', 3);
classIdContains3({'class_id': [1, 2, 3]}) // true
classIdContains3({'class_id': [1, 2]}) // false

var nameContainsE = propertyContains('name', 'e');
nameContainsE({'name': 'test 1'}) // true
nameContainsE({'name': 'Zorro'}) // false

_.filter принимает набор и критерий. Он возвращает массив со всеми элементами коллекции, которые соответствуют критерию.

_.filter(data, classIdContains3)
//[
//    {'name': 'test 1', 'address': 'New York', 'class_id': [1, 2, 3], 'country': 'USA', 'country_id': 20},
//    {'name': 'test 2', 'address': 'Lisbona', 'class_id': [2, 3], 'country': 'Portugal', 'country_id': 12},
//    {'name': 'test 5', 'address': 'New York', 'class_id': [3], 'country': 'USA', 'country_id': 20},
//]

Помимо передачи функции в качестве критерия фильтрации, такие функции, как _.filter, также позволяют использовать несколько декларативных сокращенных . Для проверки свойств на соответствие точному значению действует следующая запись:

_.filter(data, {'country_id': 20})
//[
//    {'name': 'test 1', 'address': 'New York', 'class_id': [1, 2, 3], 'country': 'USA', 'country_id': 20},
//    {'name': 'test 3', 'address': 'New York', 'class_id': [2], 'country': 'USA', 'country_id': 20},
//    {'name': 'test 4', 'address': 'Atlanta', 'class_id': [2], 'country': 'USA', 'country_id': 20},
//    {'name': 'test 5', 'address': 'New York', 'class_id': [3], 'country': 'USA', 'country_id': 20},
//]

Чтобы сузить результаты по нескольким критериям, просто отфильтруйте несколько раз.

// first filter pass
var hasClassId3 = _.filter(data, classIdContains3);

// second filter pass (filtering result of the above)
_.filter(hasClassId3, {'country_id': 20})
//[
//    {'name': 'test 1', 'address': 'New York', 'class_id': [1, 2, 3], 'country': 'USA', 'country_id': 20},
//    {'name': 'test 5', 'address': 'New York', 'class_id': [3], 'country': 'USA', 'country_id': 20},
//]

_.chain и _.value позволяют использовать более короткое обозначение вышеуказанного.

_.chain(data)
    .filter(classIdContains3)
    .filter({'country_id': 20})
    .value()
//[
//    {'name': 'test 1', 'address': 'New York', 'class_id': [1, 2, 3], 'country': 'USA', 'country_id': 20},
//    {'name': 'test 5', 'address': 'New York', 'class_id': [3], 'country': 'USA', 'country_id': 20},
//]

Используя _.each, мы можем превратить это в функцию, которая будет фильтровать по произвольным комбинациям критериев.

function multiFilter(data, criteria) {
    var result = _.chain(data);
    if (criteria.exact) result = result.filter(criteria.exact);
    _.each(criteria.contains, function(value, name) {
        result = result.filter(propertyContains(name, value));
    });
    return result.value();
}

multiFilter(data, {
    exact: {
        country_id: 20,
    },
    contains: {
        class_id: 3,
    },
})
//[
//    {'name': 'test 1', 'address': 'New York', 'class_id': [1, 2, 3], 'country': 'USA', 'country_id': 20},
//    {'name': 'test 5', 'address': 'New York', 'class_id': [3], 'country': 'USA', 'country_id': 20},
//]

multiFilter(data, {
    exact: {
        country_id: 20,
        address: 'New York',
    },
    contains: {
        name: '3',
    },
})
//[
//    {'name': 'test 3', 'address': 'New York', 'class_id': [2], 'country': 'USA', 'country_id': 20},
//]

multiFilter(data, {
    contains: {
        address: 'a',
    },
})
//[
//    {'name': 'test 2', 'address': 'Lisbona', 'class_id': [2, 3], 'country': 'Portugal', 'country_id': 12},
//    {'name': 'test 4', 'address': 'Atlanta', 'class_id': [2], 'country': 'USA', 'country_id': 20},
//]

Для получения дополнительных сведений о подчеркивании, функциональном программировании и поиске вы также можете обратиться к этому ответу .

...