Pure functions in javascript - javascript

I understand functions need to be pure in order to avoid side-effects. The the following function for example:
//Approach 1
buildValidationErrors(state){
let validationErrors = [];
if(state.name === null)
{
validationErrors.push("Name");
}
if(state.email === null)
{
validationErrors.push("Email");
}
if(state.mobile === null)
{
validationErrors.push("mobile");
}
return validationErrors;
}
//Approach 2
_buildError(state,itemName,validationErrors){
if(state[itemName] === null){
validationErrors.push(itemName);
}
}
buildValidationErrors1(state){
let validationErrors = [];
_buildError(state,"Name",validationErrors );
_buildError(state,"Email",validationErrors);
_buildError(state,"mobile",validationErrors);
return validationErrors;
}
In "Approach 1", you have a long function that builds an array. In "Approach 2" I am extracting the reusable logic to "_buildError" to avoid duplication of logic.
However, in Approach 2, the parameter validationErrors is passed in and it is updated as well causing the function to become 'impure' to my understanding.
From that perspective, can the function be pure and compact?

You can avoid passing the errors array by merging the results outside the _buildError() function:
_buildError(state,itemName){
return state[itemName] === null ? itemName : null;
}
buildValidationErrors1(state){
let validationErrors = [];
validationErrors.push(
_buildError(state,"Name"),
_buildError(state,"Email"),
_buildError(state,"mobile")
).filter((a)=> a !== null);
return validationErrors;
}
However, that does not really change the purity of the function. In your 2nd example, the function depends and change only its parameters, thus it is "pure" enough for automated tests and other practical purposes.

I would consider
_buildError(state,itemName){
return state[itemName] === null ? itemName : null;
}
// reduce if you need more than one thing in the validation array
let validationErrors = ["Name","Email","mobile"]
.reduce((acc,item) => {
if (_buildError(state,item)) acc.push({item,something:state[item].something}); return acc },
[] );
// filter if you just need the item name
let validationErrors = ["Name","Email","mobile"]
.filter(item => _buildError(state,item));

Related

why my lodash cloneDeepWith method only run once?

I have a very very very deep nested object state.
and i want to change all id properties at once with lodash cloneDeepWith methods.
i'm using cloneDeepWith and only works on first match.
if i dont return the modified object then it won't modifiy anything.
and if i return the value i think the function stops.
the function its working ok but the only problem is that only will run once.
const handleChangeIds = (value) => {
if (value === sections) {
const modifiedObject = cloneDeepWith(value, (sectionsValue) => {
if (sectionsValue && Object.hasOwn(sectionsValue, 'id')) {
const clonedObj = cloneDeep(sectionsValue);
clonedObj.id = generateObjectId();
return clonedObj;
// I Also Tried sectionsValue = clonedObj; its the same behavior
}
});
return modifiedObject;
}
};
const DuplicateSection = () => {
console.log('Original Store', form);
const store = cloneDeepWith(form, handleChangeIds);
console.log('Modified', store)
};
For those who want to achieve same thing like me.
I had a super deep nested object for form. and that form had a repeatable functionality.
and i needed to do two thing in generating another form.
generate new Id for every field Id.
clear the input Value.
I solved my problem like this
and it works perfectly for a super deep nested object.
import cloneDeepWith from 'lodash/cloneDeepWith';
const clearInputAndChangeId = (sections: FormSectionProps): FormSectionProps => {
return cloneDeepWith(sections, (value, propertyName, object) => {
if (propertyName === 'id') return generateObjectId();
if (propertyName === 'selected') return false;
if (propertyName === 'checked') return false;
if (propertyName === 'value') {
if (object.type === 'file') return [];
if (object.type === 'checkbox/rating') return 1;
return '';
}
});
};

How to efficiently validate all args in Javascript functions?

I work with Javascript (Typescript), more specifically with React. So this question is written in Javascript but it's more like a general question.
I'm trying to refactor some code and extract static methods. Once I extract the method, I find myself checking if all arguments received are valid. This becomes hard to read and, sometimes, the real purpose of the function has only a few lines.
Here's an example of how I would end up writing a method / function
const isGreaterThan = (value1, value2) => {
if(typeof value1 !== 'number'){
console.error("Invalid argument. value1 must be a number");
return 0;
}
if(typeof value2 !== 'number'){
console.error("Invalid argument. value2 must be a number");
return 0;
}
return value1 > value2
}
Now imagine if this method receives an Array
const isGreaterThanAll = (values, value1) => {
if(!values instanceof Array){
console.error("Invalid argument. values must be an Array");
return 0;
}
if(!values.every(value => typeof value === 'number')){
console.error("Invalid argument. elements of values must be numbers");
return 0;
}
if(typeof value1 !== 'number'){
console.error("Invalid argument. value2 should be type of number");
return 0;
}
return values.every(value => value1 > value);
}
Now imagine passing an Object or an Array of Objects. Depending on the complexity of the arguments, this becomes harder to follow.
Some of my questions are:
Which is the best practice to refactor code in this scenario?
How can I refactor this kind of code to be more readable?
Should I build a method to check arguments?
One way to do it is to create a recursive function like this.
const isNumberOrArrayOfNumbers = (x) => {
if (typeof x === 'number') {
return true;
}
if ( Array.isArray(x) ) {
return x.every(n => isNumberOrArrayOfNumbers(n));
}
return false;
};
const isGreaterThanAll = (values, value1) => {
let valid = [values, value1].every(a => isNumberOrArrayOfNumbers(a));
if (!valid) {
console.error('Invalid');
return Number.NaN;
}
return values.every(value => value1 > value);
};
console.log( isGreaterThanAll([1,2,3,5,6,7],12) );
console.log( isGreaterThanAll([1,2,3,5,6,7],4) );
console.log( isGreaterThanAll([1,2,3,"r",5,6,7],12) );
If you're willing to change the signature of the function you could also refactor by making it variadic and using rest operators
That might look something like this:
const isGreaterThanAll = (n, ...m) => {
let valid = [n, ...m].every(x => typeof x === 'number');
...
};

How to Reassign Function Parameters

I understand that it can be bad to reassign function parameters but I don't quite see how it would be done in this case? I'm using a forEach loop to cycle through the todo list array (which is on an object) todos and alter the completed property and I don't see how I can not reuse eachTodo
How would this be rewritten so that it has the same functionality but doesn't reuse eachTodo?
this.todos.forEach((eachTodo) => {
if (completedTodos === totalTodos) {
eachTodo.completed = false;
} else {
eachTodo.completed = true;
}
});
Full project here
You are not reassigning parameters here. If you were reassigning them, there would be some line with eachTodo = in it - but that's not the case here. Rather, you're mutating the eachTodo parameter.
If you want to avoid mutating the parameter as well, one option would be to use .map to create a copy of each eachTodo, and then reassign this.todos outside of the forEach call:
this.todos = this.todos.map((eachTodo) => {
if (completedTodos === totalTodos) {
return { ...eachTodo, completed: false };
} else {
return { ...eachTodo, completed: true };
}
});
(make sure there are no other references to individual todos to avoid memory leaks)
Your code doesn't assign to eachTodo, so I don't see how the link to the discussion about reassigning parameters is relevant.
What do you mean by "reuse eachTodo"? If you mean you want code that mentions the variable name less often, here's one way:
if (completedTodos === totalTodos) {
eachTodo.completed = false;
} else {
eachTodo.completed = true;
}
can be reduced (by pulling out the common eachTodo.completed = part) to:
eachTodo.completed = completedTodos === totalTodos ? false : true;
This line can be simplified further (as a general rule, whenever you have a ?: operator where one of the branches is just true or false, it can be simplified):
eachTodo.completed = completedTodos !== totalTodos;

Return array of functions as an array of booleans

So I'm working on updating some old projects and I am trying to find a source or an example of something I'm trying to accomplish.
what I have
// sample object of functions
var functions = {
required : function(value){
return value.length > 0;
},
isObject : function(value){
return typeof value == 'object';
}
};
Above is a sample of functions in an object. What I want to know is can the following be done:
pseudo code
//user input
var funcs = [required(''), isObject({key : 'v'})];
// what the function I'm building will process, in a sense
functions[funcs[i]](//arguments from funcs[i]);
// what would remain after each function
funcs = [false, true] // with an end result of false
I'm not 100% sure that this can't be done, I'm just not sure how in the slightest something like this would be able to come about. Let's bounce some ideas around here and see what we come up with.
Let me know if you all need any clarification of anything I asked. Thank you ahead of time for all help!
clarification on what I am trying to achieve
The object of functions is not finite, there can be any amount of functions for this specific program I am writing. They are going to be predefined, so user input is not going to be an issue. I need to be able to determine what function is called when it is passed, and make sure any arguments passed with said function are present and passed as well. So when I pass required(''), I need to be able to go through my object of functions and find functions['required'] and passed the empty string value with it. So like this functions['required']('').
other issues
The functions object is private access and the user won't have direct access to it.
How about this.
var functions = {
required : function(value){
return value.length > 0;
},
isObject : function(value){
return typeof value == 'object';
}
};
// Because these values are user inputs, they should be strings,
// so I enclosed them in quotes.
var funcs = ["required('')", "isObject({key: 'v'})"];
funcs.map(function(e) {
return eval('functions.' + e);
});
Running this should gives you an array of return values from the functions in the object.
Trivially, this could be done with:
var tests = [functions.required(''), functions.isObject({key: 'v'})];
If that's all you need, consider that my answer.
For a more general approach, the right tool here seems to be Arrays.prototype.map(). However, since you have an object containing all your functions instead of an array of functions, you'll need some way to make the correspondence. You can easily do this with a separate array of property names (e.g., ['required', 'isObject']). Then you could do something like this:
var functions = {
required : function(value){
return value.length > 0;
},
isObject : function(value){
return typeof value == 'object';
}
};
var args = ['', {key: 'v'}];
var results = ['required', 'isObject'].map(
function(test, i) {
return functions[test](args[i]);
}
);
Of course, if functions were an array instead of an object, you could simplify this:
var functions = [
/* required : */ function(value){
return value.length > 0;
},
/* isObject : */ function(value){
return typeof value == 'object';
}
];
var args = ['', {key: 'v'}];
var results = functions.map(
function(test, i) {
return test(args[i]);
}
);
If you wanted to encapsulate this a bit, you could pass the args array as a second argument to map(), in which case inside the function you would use this[i] instead of args[i].
Sure it's possible. Something like this:
var results = [];
var value = "value_to_pass_in";
for(var f in functions)
{
results.push(f.call(this, value));
}
UPDATE:
function superFunc(value)
{
var results = [];
for(var f in functions)
{
results.push(f.call(this, value));
}
return results;
}
superFunc("value_to_pass_in");
What you want is a map function. You can mimic it like this (I guess if you want one line):
https://jsfiddle.net/khoorooslary/88gh2yeh/
var inOneLine = (function() {
var resp = {};
var i = 0;
var fns = {
required : function(value){
return value.length > 0;
},
isObject : function(value){
return typeof value == 'object';
}
};
for (var k in fns) resp[k] = fns[k](arguments[i++]);
return resp;
}).apply(null, [ '' , {key : 'v'}]);
console.log(inOneLine);
var functions = {
required : function(value){
return value.length > 0;
},
isObject : function(value){
return typeof value == 'object';
}
};
var funcs = ["required('')", "isObject({key: 'v'})"];
function f(funcs){
return funcs.map(function(e) {
return eval('functions.' + e);
});
}
console.log(f(funcs));

lodash where method to consider nested array elements [duplicate]

I have an object of folders/files that looks like this:
{
about.html : {
path : './about.html'
},
about2.html : {
path : './about2.html'
},
about3.html : {
path : './about3.html'
},
folderName : {
path : './folderName',
children : {
sub-child.html : {
path : 'folderName/sub-child.html'
}
}
}
}
And it can go 6-7 levels deep of folders having children.
I want to find the object where path is equal to a string that I provide. Regardless of how deep it is.
I'm using underscore which only does top level:
_.findWhere(files,{path:'./about2.html'}
How can I do a deep, nested search. Does underscore have something for this or do I need to build a mixin with recursion?
This isn't the prettiest code, but I tested it out and it seems to work the way you are asking. It's setup as a lodash/underscore mixin, but can be used however. Usage would be like this:
_.findDeep(testItem, { 'path': 'folderName/sub-child.html' })
Implementation:
findDeep: function(items, attrs) {
function match(value) {
for (var key in attrs) {
if(!_.isUndefined(value)) {
if (attrs[key] !== value[key]) {
return false;
}
}
}
return true;
}
function traverse(value) {
var result;
_.forEach(value, function (val) {
if (match(val)) {
result = val;
return false;
}
if (_.isObject(val) || _.isArray(val)) {
result = traverse(val);
}
if (result) {
return false;
}
});
return result;
}
return traverse(items);
}
Instead of findWhere, use filter, which takes a function as the predicate rather than a key-value map. Use a recursive function to check the current node and possible children. Something like this:
var searchText = './about2.html';
var recursiveFilter = function(x) {
return x.path == searchText ||
( typeof x.children != 'undefined' && recursiveFilter(x.children['sub-child.html']) );
};
_.filter(files, recursiveFilter);
Edit
Assuming this works, you'll probably want to make a function getRecursiveFilter(searchText). Here's how that would look:
function getRecursiveFilter(searchText) {
var recursiveFilter = function(x) {
return x.path == searchText ||
(typeof x.children != 'undefined'
&& arguments.callee(x.children['sub-child.html']) );
};
return recursiveFilter;
}
Note that here, recursiveFilter uses arguments.callee to call itself recursively.
Here's a working demo.
This already has an accepted answer, but this other answer was very clean and perfect for my similar situation: https://stackoverflow.com/a/21600748/1913975
_.filter +_.where
Though accepted answer works, it's too generic - it searches all the properties of an object to find children. I am proposing introducing an extra parameter, called 'recursProperty' which will be considered to go deep in the object. This solution is also setup to be used as lodash/underscore mixin and extends loadash/underscore capabilities.
_.findDeep = function(collection, predicate, recursProperty){
let items = [];
_.each(collection, each => items.push(each));
return _.find(items, function(value, key, coll){
if (predicate(value, key, coll)){
return true;
} else {
_.each(value[recursProperty], each => items.push(each));
}
});
};
It can be used as any other underscore function. e.g,
_.findDeep(self.baseEntities, baseEntity => baseEntity.id === 71, 'entity');
Not providing proper value for 'recursProperty' argument or providing null/undefined will simply make the search only on first level (no going deep).

Categories