Refactoring two methods using indexOf - javascript

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

Related

Dynamic Function Watcher in JS

I'm working on a pet project, a little front-end library for students. It reads variables/code in a JS file and tests it, outputting some panels. The code itself roughly follows the Jest framework.
My problem is that I'm trying to create a function that watches the execution of other functions, counts them, and lets me access the count.
function watchFunction(funcName){
let originalFunction = window[funcName];
let counter = 0;
// Wrap the function, counts when called
window[funcName] = function(...args){
console.log("watching");
counter++;
return originalFunction(...args);
}
return {
getCount: () => {return counter},
reset: () => {
// Unwrap the function
window[funcName] = originalFunction
}
}
}
This seems to work for methods like Number() or parseInt(), but I don't know how I would go about accessing methods like Math.floor(), or prototype methods like Array.prototype.map().
I've tried passing in the function reference instead of using window["funcNameString"], but that doesn't seem to work.
Does anyone have suggestions or tips for wrapping functions or watching functions like this?
EDIT:
It appears a solution was found!
function watchFunction(obj, fName) {
let counter = 0;
const originalFunction = obj[fName];
obj[fName] = (...args) => {
counter++;
return originalFunction.bind(obj)(...args);
};
return {
removeWatcher: () => (obj[fName] = originalFunction),
resetCount: () => (counter = 0),
getCount: () => counter,
};
}
Example of use:
// Array.prototype.push
const arrayPushWatcher = watchFunction(Array.prototype, "push");
let arr = [];
// 0
console.log("Array.prototype.push", arrayPushWatcher.getCount());
arr.push(1);
// 1
console.log("Array.prototype.push", arrayPushWatcher.getCount());
arr.push(1);
// 2
console.log("Array.prototype.push", arrayPushWatcher.getCount());
arrayPushWatcher.removeWatcher();
arr.push(1);
// 2 (stopped counting)
console.log("Array.prototype.push", arrayPushWatcher.getCount());
How to watch for any function call
Is that what you want? I can also write a block for this function so that it determines whether an object has been passed in or a string. If string -> run this function on window as a property "objectThatStoresFunction".
I've tried playing around with the Function.prototype, but it doesn't really work. So the function turned out a bit more complex.
This code below works both with functions / objects on window Array.prototype.map (Prototype / Class functions)
function watchFunction(objectThatStoresFunction, functionName) {
let counter = 0;
const originalFunction = objectThatStoresFunction[functionName];
objectThatStoresFunction[functionName] = (...args) => {
counter += 1;
return originalFunction(...args);
}
return {
getCount: () => {
return counter
}
}
}
const mathRoundWatcher = watchFunction(Math, 'round');
// 0
console.log(mathRoundWatcher.getCount());
// 1
Math.round(99666.9999999);
console.log(mathRoundWatcher.getCount());
// 2
Math.round(999999999.99);
console.log(mathRoundWatcher.getCount());
function watchFunction(objectThatStoresFunction, functionName, optionalOriginalFunction) {
const self = this;
if (optionalOriginalFunction) {
objectThatStoresFunction = this.window;
functionName = optionalOriginalFunction.name;
}
let counter = 0;
const originalFunction = objectThatStoresFunction[functionName] || optionalOriginalFunction;
objectThatStoresFunction[functionName] = (...args) => {
counter += 1;
return originalFunction.bind(self)(...args);
}
return {
// should it remove the watcher or reset the count?
reset: () => objectThatStoresFunction[functionName] = originalFunction,
getCount: () => {
return counter;
}
}
}
const arrayMapWatcher = watchFunction(Array.prototype, 'map');
// 0
console.log('Array.prototype.map', arrayMapWatcher.getCount());
[-99].map(() => {});
// 1
console.log('Array.prototype.map', arrayMapWatcher.getCount());
const mathRoundWatcher = watchFunction(Math, 'round');
// 0
console.log('Math.round', mathRoundWatcher.getCount());
// 1
Math.round(99666.9999999);
console.log('Math.round', mathRoundWatcher.getCount());
// 2
Math.round(999999999.99);
console.log('Math.round', mathRoundWatcher.getCount());
const alertWatcher = watchFunction(null, null, window.alert);
// 0
console.log('window.alert', alertWatcher.getCount());
// 1
window.alert('1');
console.log('window.alert', alertWatcher.getCount());
// 2
alert('2')
console.log('window.alert', alertWatcher.getCount());
// reset the alertWatcher counter
alertWatcher.reset();
This code above breaks the stacksnippets.com when used with Array.prototype.map for some reason, please see this JsFiddle link:
https://jsfiddle.net/ctbjnawz/3/
Do you mean a method of an instance or object? One way is to create a new function. e.g
function WatchInstanceMethods(instance, functionName){
let originalFunction = window[instance][funcName];
let counter = 0;
window[instance][functionName] = function(...args){
console.log("watching");
counter++;
return originalFunction(...args);
}
return {
getCount: () => {return counter},
reset: () => {
// Unwrap the function
window[funcName] = originalFunction
}
}
}
although adding support for chaining methods will get difficult with more nested methods but you can pass a string for functionName name and split it to have each layer of calling instance for function and repeat the logic above.

Simplifying a nested loop in a javascript function

I have this function in JS
function getMap(objectList) {
const objectMap = new Map();
IDS.foreach(id => {
const attribute = objectList.find(object => object.getId() === id);
if (attribute) {
objectMap.set(id, attribute);
} else {
objectMap.set(id, null);
}
}
This is a nested loop because of the find inside the for loop. How could this be simplified? If the nested loop cannot be simplified, can other parts be simplified?
Assuming object IDs are unique, it looks like all you really have to do is call getId on each object beforehand. The conditional operator may be used instead of if/else if you wish.
function getMap(objectList) {
const objectsById = new Map(
objectList.map(object => [object.getId(), object])
);
const objectMap = new Map();
for (const id of IDS) {
objectMap.set(id, objectsById.get(id) || null);
}
}
You could create an array with null entries for each ID, followed by entries for which you actually have values in objectList, and pass that array to the Map constructor:
function getMap(objectList) {
return new Map([
...IDs.map(id => [id, null]),
...objectList.map(object => [object.getId(), object])
]);
}
Using native code with a simple callback
const result = (IDS || []).map(function(id, idx, arr) {
const pos = (objectList || []).findIndex(object => object.getId() === id);
const output = [];
output[id] = (pos >= 0 ? objectList[pos] : null);
return output;
});
Hope this helps... ;D

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

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

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)
}
}

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