Вы не указываете, как сортировать совпадения.Одной из возможностей является сортировка по количеству совпадений.Оказывается, это довольно просто.Мой первый проход выглядит так:
const countMatches = (target) => ({tags}) =>
tags .reduce ( (n, tag) => target .includes (tag) ? n + 1 : n, 0)
const descendBy = (fn) => (xs) =>
xs .slice (0)
.sort( (a, b) => fn (b) - fn (a) )
const sortByTagMatches = (target) => descendBy ( countMatches (target) )
sortByTagMatches (tags) (posts) //=> sorted results
countMatches
Функция countMatches
просто подсчитывает количество совпадений между целью (tags
) и сообщением tags
имущество.Всегда существует противоречие между использованием наиболее конкретного кода, который выполняет свою работу, и более общей, многократно используемой версией.Но здесь разница между более общей функцией:
const countMatches = (target, name) => (o) =>
o[name] .reduce ( (n, x) => target .includes (x) ? n + 1 : n, 0)
и конкретной:
const countMatches = (target) => ({tags}) =>
tags .reduce ( (n, tag) => target .includes (tag) ? n + 1 : n, 0)
настолько мала, а разница в использовании между ними почти такая же незначительная -что если есть любой шанс, что я захочу использовать эту функциональность в другом месте моего приложения, я бы выбрал эту универсальную.
Есть еще одно упрощение, которое мы могли бы включить:
const countMatches = (target, name) => (o) =>
o[name] .filter ( (x) => target .includes (x) ) .length
Это немного более простой код.Функция, переданная в filter
, несомненно, чище, чем функция, переданная в reduce
.Но есть компромисс.filter
создает новый массив сообщений, использует его только для получения length
, а затем выбрасывает его.В горячем коде это может быть проблемой производительности, но в большинстве случаев это просто неправильно, если вызов reduce
не , который намного сложнее, чем filter
.
descendBy
descendBy
просто.Вы передаете ей целочисленную функцию, и она возвращает функцию, которая сама принимает массив значений и возвращает отсортированную версию этого массива.Он не изменяет массив на месте.Если вы действительно этого хотите, вы можете просто удалить вызов slice
.Эта функция основана на функции, которую я часто использую, под названием sortBy
, только с вычитанием, обратным для снижения.Я вполне мог бы включить оба в проект, хотя, если бы я это сделал, я мог бы переименовать sortBy
в ascendBy
, чтобы прояснить параллель.
Нетрудно сделать более общую версию этой функции.если нам нравитсяВместо того, чтобы принимать функцию, которая возвращает число, мы могли бы иметь функцию, которая принимает функцию, которая возвращает любое упорядоченное значение, Даты, Строки, Числа и все, что реализует valueOf
, по существу, все, что может быть полезнопо сравнению с "<"
.(Это иногда называют упорядоченным - или Ord
- типом).Эта версия может выглядеть так:
const descendBy = (fn) => (xs) =>
xs .slice (0)
.sort( (a, b, x = fn (a), y = fn (b) ) => x < y ? 1 : x > y ? -1 : 0 )
Здесь я стою перед вопросом о том, стоит ли предпочитать универсальную версию во время моего первого прохода.Конкретный выше действительно проще.Я бы, вероятно, использовал конкретный, зная, что он совместим с общим, если мне когда-нибудь понадобится его заменить.
Дальнейшее упрощение
descendBy
, кажется, выполняет слишком много работы.Он преобразует Ord
функцию возврата в компаратор, а затем сортирует список с использованием этого компаратора.Было бы неплохо разбить эти два шага на части, сделав результат descendBy
еще более пригодным для повторного использования.Здесь название descend
кажется более правильным:
const descend = (fn) =>
(a, b) => fn(b) - fn(a)
const sortByTagMatches = (target) => (xs) =>
xs .slice(0) .sort (descend (countMatches (target, 'tags') ) )
Мы переместили нарезку и сортировку в основную функцию, оставив descend
довольно простым.И вот тут я думаю, что оставлю это.Код теперь выглядит так:
const countMatches = (target, name) => (o) =>
o[name] .filter ( (x) => target .includes (x) ) .length
const descend = (fn) =>
(a, b) => fn(b) - fn(a)
const sortByTagMatches = (target) => (xs) =>
xs .slice(0) .sort (descend (countMatches (target, 'tags') ) )
const tags = ['one', 'two', 'three']
const posts = [{tags: ['four', 'five']}, {tags: ['one', 'six']}, {tags: ['seven']}, {tags: ['one', 'three', 'five']}, {tags: ['nine', 'two']}]
console .log (
sortByTagMatches (tags) (posts)
)
(Обратите внимание, что я добавил дополнительный пост с двумя совпадающими тегами для демонстрации дополнительных функций сортировки.)
Почему?
user633183дал хороший ответ о том, почему мы должны хотеть разбить наш код на маленькие многократно используемые функции.Это просто демонстрирует тот же процесс с несколько иным представлением о том, как проблема может решиться.