attempt to compress inside of (simple) function into more efficient code - javascript

I wrote a simple function that adds and removes classes for elements (buttons).
function mainBut (){
ba.classList.add("act2");
ba.classList.remove("hov")
bh1.classList.add("hov");
bh1.classList.remove("act2");
bh2.classList.add("hov");
bh2.classList.remove("act2");
da.classList.remove("none")
dh1.classList.add("none")
dh2.classList.add("none")
}
But as the number of elements grows, i see that the code could be better organized. Because we could:
remove: .act2 for (bh1 and bh2) | add: .hov for (bh1 and bh2) | add: .none for (dh1 and dh2).
I'm wondering, if it could be done using for loop? Or maybe there is a better way...

You can use functional programming to simplify it. Break-in small function and reuse.
const add = cls => elm => elm.classList.add(cls);
const remove = cls => elm => elm.classList.remove(cls);
const addAct2 = add("act2");
const addHov = add("hov");
const removeAct2 = remove("act2");
const removeHov = add("hov");
const addNone = add("none");
const removeNone = add("none");
function mainBut() {
addAct2(ba);
removeHov(ba);
addHov(bh1);
removeAct2(bh1);
addHov(bh2);
removeAct2(bh2);
removeNone(da);
addNone(dh1);
addNone(dh2);
}
// MORE FUNTIONAL
const curry = (fn, arity = fn.length, ...args) =>
arity <= args.length ? fn(...args) : curry.bind(null, fn, arity, ...args);
const apply = curry((fn, data =[]) => data.map(fn))
function mainBut() {
apply(removeHov, apply(addAct2, [ba]))
apply(removeAct2, apply(addHov, [bh1, bh2]))
apply(removeNone, [da])
apply(addNone, [da, dh1, dh2])
}

Related

How to self-reference NodeJS Module?

I have a module that I'm exporting. I need one function to call another function. Here's a simplified version of what I'm trying to do.
module.exports = {
isEven: (number) => {
return (number%2 == 0)
},
isTenEven: () => {
return isEven(10)
}
}
The code above throws isEven is not defined when moduleName.isTenEven() is called.
It makes sense why it fails. But how would you rewrite it? (While maintaining the singleton pattern)
Define the functions first, then export them:
const isEven = (number) => number % 2 === 0
const isTenEven = () => isEven(10)
module.exports = {
isEven,
isTenEven
}
The object is only used to group the functions together. There's nothing really OO about it so define the functions separately. Construct the object at the end.
const isEven = number => number % 2 === 0;
const isTenEven = () => isEven(10);
module.exports = { isEven, isTenEven };
Maybe just do this? Define then export.
const isEven = number => number % 2 === 0;
module.exports = {
isEven,
isTenEven: () => isEven(10)
};
Just to add one more solution to the mix. You don't have to define the function elsewhere. Since the object declaration is complete before the function gets called, you can refer to it via module.exports or via exports like this:
module.exports = exports = {
isEven: (number) => {
return (number%2 === 0)
},
isTenEven: () => {
return exports.isEven(10)
}
}
If you were doing this in a lot of methods, you could define a shorter variable name for the exported object and refer to it.
If you can afford Babel or a version of Node.js that supports import/export statements you could also do:
export const isEven = num => num % 2 === 0;
export const isTenEven = () => isEven(10);
Inside JS object literal using this refers to the object itself so you can have:
module.exports = {
isEven: (number) => {
return (number%2 == 0)
},
isTenEven: function () {
return this.isEven(10)
}
}

Functional Ramda Passing function parameter outside of scope

Take the following example imperative JavaScript example:
getAnimalList = (hasCat) => {
const baseAnimals = { dog: animLib.dog(), bear: animLib.bear()};
if(hasCat){
baseAnimals.cat = animLib.cat();
}
return baseAnimals
}
I am trying to write this code in a functional style with Ramda, but the only way I can do so is by having the functions reach outside of scope:
getAnimalList = (hasCat) => {
const baseAnimals = { dog: animLib.dog(), bear: animLib.bear()};
return when(always(hasCat), merge({hasCat: animLib.cat()}))(baseAnimals)
}
Leaving aside how animLib is outside of scope, the way I would fix has hasCat from being pass from the outside(if this is a problem at all) is the following:
getAnimalList = (hasCat) => {
const baseAnimals = { dog: animLib.dog(), bear: animLib.bear()};
const mergeCat = when(compose(equals(true), prop('hasCat')),
compose(merge({hasCat: animLib.cat()}), prop('baseAnimals')));
return mergeCat({hasCat: hasCat, baseAnimals: baseAnimals});
}
But this makes the code incredibly verbose. Is there a better way to do this? Or is the verbosity just the cost of keeping the code more pure.
It seems to me that this would do fine:
getAnimalList = (animLib, hasCat) => {
const baseAnimals = { dog: animLib.dog(), bear: animLib.bear()};
return hasCat ? merge({hasCat: animLib.cat()}, baseAnimals) : baseAnimals;
}
Because Ramda's when and ifElse are based around predicate functions rather than boolean values, using them seems to be overkill here.
Obviously if you want a version that has animLib in scope, you can curry this function, either with something like Ramda's curry or just by changing to
getAnimalList = (animLib) => (hasCat) => { /* ... */ }
Then you can create newAnimalList = getAnimalList(myAnimLib).

Get to intermediate values in middle of a functional programming chain

I'm wondering if there's a concise or specific way to access values in the middle of an FP chain in JavaScript. Example:
const somestuff = [true, true, false];
let filteredCount = 0;
somestuff.filter((val) => val)
.forEach((val) => console.log(val));
Above, I'd like to set filteredCount to the length of the array returned by the filter function. The most straight-forward way is:
const somestuff = [true, true, false];
const filteredStuff = somestuff.filter((val) => val);
let filteredCount = filteredStuff.length;
filteredStuff.forEach((val) => console.log(val));
This is certainly valid but it breaks our FP chain and introduces an additional holding variable. I'm wondering if there's a convention for accessing values in the middle of the chain. Something like .once() that runs once and implicitly returns the value passed in, but nothing like that exists.
For debugging, I often use a function called tap to temporarily add a side-effect (like your console.log) to a function:
const tap = f => x => (f(x), x);
This function returns whatever it is passed, but not before calling another function with the value. For example:
const tap = f => x => (f(x), x);
const tapLog = tap(console.log);
const x = tapLog(10);
console.log("x is", x);
Your snippet basically does this:
Filter a list
(log the list)
Retrieve a length property from an array
If you construct this function using pipe or compose, you can "inject" the console.log in between without interrupting the data flow:
const countTrues = pipe(
filter(isTrue),
prop("length")
);
const countTruesWithLog = pipe(
filter(isTrue),
tap(console.log),
prop("length")
);
In a snippet:
// Utils
const isTrue = x => x === true;
const prop = k => obj => obj[k];
const tap = f => x => (f(x), x);
const filter = f => xs => xs.filter(f);
const pipe = (...fns) => x => fns.reduce((res, f) => f(res), x);
// Logic:
// Filter an array using the isTrue function
// and return the length of the result
const countTrues = pipe(
filter(isTrue),
prop("length")
);
// Create a filter with a console.log side-effect
// and return the length of the result
const countTruesWithLog = pipe(
filter(isTrue),
tap(console.log),
prop("length")
);
// App:
const somestuff = [true, true, false];
console.log("pure:");
const countA = countTrues(somestuff)
console.log(countA);
console.log("with log:")
const countB = countTruesWithLog(somestuff);
console.log(countB);
The reason there's no Array.prototype method like that, is that it has a side effect. This is something that is specifically avoided in functional programming.
However if you don't care about writing 'Pure Functions', or even the functional paradigm, you could put the side effect in your callbacks, or write a function in the Array prototype.
ie.
Array.prototype.once = function(callback) {
callback(this)
return this
}
You also have other hacky options like in the other answer
I don't think there's something like that by default. What you can do is extend Array, but I'm not really fond of extending framework classes (clashes with other once implementations for example). In this case you'd end up with:
Array.prototype.once = function once(func) {
func(this);
return this;
}
which is called like:
var filteredStuff = somestuff
.filter((val) => val)
.once(function(array) {
console.log(array.length);
})
.forEach((val) => console.log(val));
On the other hand, you can try to use default functions. One of these function that can access all items at once is reduce. Define a function once, that will call its first parameter once (:)) and you'd end up with something like:
function once(func) {
return function(accumulator, currentValue, currentIndex, array) {
if(currentIndex === 1) {
func(array);
}
return array;
}
}
which you'd be able to call like this:
var filteredStuff = somestuff
.filter((val) => val)
.reduce(once(function(array) {
console.log(array.length);
}), [0])
.forEach((val) => console.log(val));
Notice the ugly [0] to ensure once calls the passed function at least once (empty array included).
Both solutions aren't too neat, but it's the best I can come up with given the criteria.

Refactoring two methods using indexOf

These two methods are almost identical, how could they be refactored into one method?
method1 = (value) => {
var example = array.filter(row => row.key.indexOf(value) > -1);
this.setState({state: example});
}
method2 = (value) => {
var example = array.filter(row => row.otherKey.indexOf(value) > -1);
this.setState({otherState: example});
}
Take what varies between the two functions and separate it from the function by making those items arguments that must be passed into the function:
var method = (value, key, state) => {
var example = array.filter(row => row[key].indexOf(value) > -1);
this.setState({state: example});
}

How to return the same variable across multiple functions

How can I return __plugin inside add and css function without return __plugin inside them. Can I use a higher order function? Everything is working as expected but I would like to know and maybe refactor this code.
I don't want to write return __plugin every time only one time.
const $ = (el) => {
let _plugin = {};
const element = document.querySelector.bind(document);
const css = (style) => {
Object.assign(element(el).style, style)
return _plugin;
}
const add = (c) => {
element(el).classList.add(c)
return _plugin;
}
_plugin = { add, css }
return _plugin
}
Thank you.
You can create a higher-order function which wraps around each of the functions in your _plugin object, and it will make all of your functions chainable.
const chainable = (obj) => {
for (const key in obj) {
const func = obj[key];
if (typeof func === 'function') {
obj[key] = function () {
const val = func.apply(this, arguments);
return val === undefined ? this : val;
};
}
}
return obj;
};
const $ = (el) => {
const element = document.querySelector(el);
const css = (style) => {
Object.assign(element.style, style);
};
const add = (c) => {
element.classList.add(c);
};
return chainable({ add, css });
};
/* Testing code */
$('#test')
.add('testClass')
.css({ 'background-color': 'black' });
.testClass { color: red; }
<div id="test">Testing</div>
I'm assuming you want to attach objects to those variables with something like this:
const $ = (el) => {
const element = document.querySelector.bind(document);
const css = (style) => {
Object.assign(element(el).style, style);
};
const add = (c) => {
element(el).classList.add(c);
};
return {add, css};
};
Otherwise you'd just be returning undefined objects since plugin is never manipulated (or dependent on multiple conditions rather).
Based on the comments.. you could do a minor refactor like so:
const $ = (el) => {
const element = document.querySelector.bind(document);
const css = function(style) {
Object.assign(element(el).style, style)
return this;
}
const add = function(c) {
element(el).classList.add(c)
return this;
}
return { add, css };
};
If you want to get rid of using plugin and keep the ability to chain, swap out the inner arrow function for regular functions. This allows you to return this which is scoped to $.

Categories