Kirill Monocle
Kirill Monocle

Reputation: 77

More elegant way to get filtered value?

I have a function that returns the filtered result based on string in the object (if it exist or not)

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

So it's just return all objects in array that contains 'foo' in classes object This one, for example:

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'}
]

But my guts tells that the function possible to write more easily and succinctly. Any ideas?

Upvotes: 2

Views: 99

Answers (3)

Mulan
Mulan

Reputation: 135207

Per usual, @NinaScholz offers a great, straightforward approach. You'd be well-off following her advice.

I personally like to dissect these problem a little further and demonstrate how smaller, reusable functions can be combined to achieve the desired effect. It's my hope that this answer will help you see that functional programming in JavaScript easily extends beyond the built-in prototypal methods.

Comments help you understand types but are completely optional.

// 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));

Upvotes: 1

Thomas
Thomas

Reputation: 12637

As an alternative to "filter some", as shown by Nina, a slightly different approach.

Doesn't look that clean, but is more performant (as far as performance matters at such a simple task).

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 '));

Upvotes: 0

Nina Scholz
Nina Scholz

Reputation: 386560

You could use just 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);

Upvotes: 5

Related Questions