Итеративные методы Collection могут принимать множество различных параметров, но один из самых интересных является filter, давайте рассмотрим на примере:
// Простой map $C([1, 2, 3, 4, 5]).map(function (el) { return el * 2; }) // [2, 4, 6, 8, 10] // А теперь только для тех элементов, которые больше 2 $C([1, 2, 3, 4, 5]).map(function (el) { return el * 2; }, {filter: function (el) { return el > 2; }}) // [6, 8, 10]
Т.е. на любой итеративный метод, будь то map, reduce, length и т.д. можно наложить дополнительную функцию фильтр, которая будет отсеивать «лишние» элементы.
Ещё примеры:
// Количество элементов в объекте, значение которых больше 2 $C({a: 1, b: 2, c: 3, e: 4}).length(function (el) { return el > 2; }) // 2 // Количество вхождений каждого символа строки, кроме "o" $C('foo Bar').group(fuction (el) { return el; }, {filter: function (el) { return el !== 'o'; }})
Но это ещё не всё, Collection позволяет заранее продекларировать все необходимые фильтры, а затем вызывать их по ИД и даже комбинировать. Давайте напишем 2 фильтра: первый будет находить уникальные или не уникальные элементы в исходной коллекции, а второй «склеивать» дубликаты (сразу скажу, что я не преследую здесь цель написать наиболее оптимальный алгоритм, а просто как пример).
// Декларируем наши фильтры с помощью метода addFilter $C().addFilter('unique', function (el, key, data, i, length) { // Параметр this.$ содержит ссылку на пустой объект, // который создаётся в рамках каждого итератора для использования в мемоизации (т.е. сохранение промежуточных значений) // this.$.ready не определён, значит идёт первых проход итератора if (!this.$.ready) { let cache = this.$.cache = this.$.cache || new Set(); let final = this.$.final = this.$.final || new Set(); if (cache.has(el)) { final.delete(el); } else { final.add(el); cache.add(el); } // Параметр this._ содержит ссылку объект, // который содержит характеристики текущей операции (тип данных, различные ограничители и т.д.) // Если идёт последняя итерация, то сбрасываем операцию и начинаем проход заново if (i === (this._.endIndex || (length() - 1))) { this.reset(); this.$.ready = true; } // this.FALSE специальная константа, которая означает, // что фильтр всегда должен возвращать false, даже если его инвертировали return this.FALSE; } // Идёт второй проход и у нас уже есть индексирующий объект return this.$.final.has(el); });
Теперь давайте попробуем в действии
// Метод get возвращает массив элементов коллекции, которые подходят под фильтр $C([1, 2, 2, 3]).get('unique') // [1, 3] // Только не уникальные $C([1, 2, 2, 3]).get('!unique') // [2, 2]
А теперь напишем фильтр, который будет «склеивать» одинаковые элементы в один
$C().addFilter('merge', function (el) { this.$.tmp = this.$.tmp || new Set(); if (!this.$.tmp.has(el)) { this.$.tmp.add(el); // this.TRUE антоним this.FALSE return this.TRUE; } return this.FALSE; });
И сейчас можно запустить их вместе
// Только не уникальные $C([1, 2, 2, 3, 5, 5]).get('!unique && merge') // [2, 5]
Следует заметить, что в составных фильтрах можно также использовать «или» ||
и круглые скобки для группировок, на которые также можно накладывать знак инверсии !
.
Допускается создавать фильтры, которые ссылаются на другие
$C().addFilter('mix', '!unique && merge');
Ну и напоследок: можно назначать «активные» фильтры, которые буду использовать по умолчанию всегда, а фильтры которые буду указаны явно, будут их просто дополнять.
$C([1, 2, 2, 3, 5, 5]).setFilter('unique').get() // [1, 3] $C([1, 2, 2, 3, 5, 5]).setFilter('!unique').get('merge') // [2, 5]
Давайте напишем условие для пагинации, скажем загрузка 2-й страницы, где на одной странице не более 10 элементов.
// Исходной коллекцией пускай будет некоторый Map $C(new Map(...)).get({ filter: '!unique && merge', from: 10, count: 10 })
Надеюсь, что эта коротенькая заметка была интересной и понятной, всем удачи!
ссылка на оригинал статьи http://habrahabr.ru/post/225995/
Добавить комментарий