Более элегантный способ получить отфильтрованное значение?

#javascript #arrays #object #functional-programming

#javascript #массивы #объект #функциональное программирование

Вопрос:

У меня есть функция, которая возвращает отфильтрованный результат на основе строки в объекте (если она существует или нет)

 let foo = nodes.reduce((arr, cur) => {
    cur.classes.split(' ').filter((el) => {
        if (el === 'foo') arr.push(cur)
    })
    return arr; 
}, []);
  

Таким образом, он просто возвращает все объекты в массиве, который содержит ‘foo’ в classes object
Этот, например:

 let nodes = [
  {node: 'h1', classes: 'foo'},
  {node: 'p', classes: 'bar'},
  {node: 'p', classes: 'baz xxx'},
  {node: 'h2', classes: 'bar baz foo'},
  {node: 'ul', classes: 'poop foo'}
]
  

Но моя интуиция подсказывает, что функцию можно написать проще и лаконичнее. Есть идеи?

Ответ №1:

Вы могли бы использовать просто Array#filter .

 let nodes = [{node: 'h1', classes: 'foo'}, {node: 'p', classes: 'bar'}, {node: 'p', classes: 'baz xxx'}, {node: 'h2', classes: 'bar baz foo'}, {node: 'ul', classes: 'poop foo'}],
    foo = nodes.filter(a => a.classes.split(' ').some(b => b === 'foo'));

console.log(foo);  

Ответ №2:

Как обычно, @NinaScholz предлагает отличный и простой подход. Вам было бы хорошо следовать ее совету.

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

Комментарии помогают вам понять типы, но они совершенно необязательны.

 // comp :: (b -> c) -> (a -> b) -> (a -> c)
const comp = f => g => x => f (g (x));

// filter :: (a -> Boolean) -> [a] -> [a]
const filter = f => xs => xs.filter(f);

// some :: (a -> Boolean) -> [a] -> Boolean
const some = f => xs => xs.some(f);

// eq :: (String a, Number a) => a -> a -> Boolean 
const eq = x => y => y === x;

// nodeClasses :: Node -> [String]
const nodeClasses = ({classes}) => classes.split(' ');

// nodesWithClass :: String -> [Node] -> [Node]
const nodesWithClass = c => filter (comp (some (eq (c))) (nodeClasses));

// nodes :: [Node]
let nodes = [
  {node: 'h1', classes: 'foo'},
  {node: 'p', classes: 'bar'},
  {node: 'p', classes: 'baz xxx'},
  {node: 'h2', classes: 'bar baz foo'},
  {node: 'ul', classes: 'poop foo'}
];

console.log(nodesWithClass('foo') (nodes));  

Комментарии:

1. @NinaScholz, в своем ответе я назвал вас женщиной, но я не хочу делать никаких оскорбительных предположений. Пожалуйста, поправьте меня, если я ошибаюсь ^_ ^

Ответ №3:

В качестве альтернативы «фильтровать некоторые», как показано Ниной, немного другой подход.

Выглядит не так чисто, но более производительно (насколько важна производительность при такой простой задаче).

 let nodes = [
  {node: 'h1', classes: 'foo'},
  {node: 'p', classes: 'bar'},
  {node: 'p', classes: 'foo-bar'},
  {node: 'p', classes: 'baz xxx'},
  {node: 'h2', classes: 'bar baz foo'},
  {node: 'ul', classes: 'poop foo'}
];

let foo = nodes.filter(node => (' '   node.classes   ' ').contains(' foo '));
  

Комментарии:

1. Также хорошее решение, спасибо, Томас. Я сохраню его. 🙂