2
\$\begingroup\$

What follows is a piece of code that essentially toggles an array of filters (if the filter doesnt exist it adds it, if it does, it removes it).

What would you suggest is the best way to write the following imperative approach declaratively?

var selectedFilters = [ {name:"SomeName"} , ... ]
var inputFilter = {name:"OtherName"};
var indexFound = -1;
for (let i = 0; i < selectedFilters.length; i++) {
 if (selectedFilters[i].name === inputFilter.name) {
 indexFound = i;
 }
}
if (indexFound != -1) {
 selectedFilters.splice(indexFound, 1);
} else {
 selectedFilters.push(inputFilter);
}

An idea would be to use filter first to weed out the item if it exists by name, then if the resulting array is equal to the original, push. But it still doesnt feel right.

200_success
145k22 gold badges190 silver badges478 bronze badges
asked Mar 27, 2018 at 11:16
\$\endgroup\$

3 Answers 3

2
\$\begingroup\$

Pure V State

There are two ways you can do this.

Pure

The first functional pure method first copies the array, then checks if the item to toggle exists then depending on that result adds or removes the item. Making sure that the added item is a copy, not a reference. It has no side effects but requires additional memory and CPU cycles.

const toggleItem = (itemDesc, items, prop = "name") => {
 items = [...items]; 
 const index = items.findIndex(item => itemDesc[prop] === item[prop]);
 index > -1 ? items.splice(index, 1) : items.push({...itemDesc});
 return items;
}

State

The second does not create a new array and keeps all references It is "functionally" impure and ensures that the changed state is available to all references to the original. It is considerably quicker and uses less memory.

const toggleItem = (itemDesc, items, prop = "name") => {
 const index = items.findIndex(item => itemDesc[prop] === item[prop]);
 index > -1 ? items.splice(index, 1) : items.push(itemDesc);
 return items;
}
answered Mar 27, 2018 at 13:27
\$\endgroup\$
2
  • \$\begingroup\$ What's the point in items = [...items]? Making arrays generally immutable and each time producing a new sequence? Shouldn't it be implemented in a more structure-sharing manner? \$\endgroup\$ Commented Mar 27, 2018 at 13:38
  • \$\begingroup\$ @bipll The array needs to be copied to prevent side effects and keep the function pure. I do not code like that (its crazily inefficient and makes coding so much harder) but for many functional is the bee's knees and it is a requirement of the functional programming paradigm. \$\endgroup\$ Commented Mar 27, 2018 at 14:09
1
\$\begingroup\$

Here is a functional version. Javascript has quite a lot functional type functions that can help you out here. It could probably made to be even nicer, but this was my first approach

function updateFilters(currentFilters, newFilter) {
 const hasName = filter => filter.name === newFilter.name;
 const foundIndex = currentFilters.findIndex(hasName);
 return foundIndex === -1
 ? currentFilters.concat([newFilter])
 : currentFilters.splice(foundIndex, 1);
}
Sᴀᴍ Onᴇᴌᴀ
29.5k16 gold badges45 silver badges201 bronze badges
answered Mar 27, 2018 at 11:38
\$\endgroup\$
0
\$\begingroup\$

Why not just filter with selectedFilters.filter(predName(inputFilter)) with a predicate predName = ({name: filterName}) => ({name}) => name === filterName.

answered Mar 27, 2018 at 12:43
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.