Simplifying a nested loop in a javascript function - javascript

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

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

Only push the object which is not in the array yet

This is my function:
const multiSelect = value => {
let tmpArr = [...selectedPeople];
if (tmpArr.length === 0) {
tmpArr.push(value);
} else {
tmpArr.map(item => {
if (item.id !== value.id) {
tmpArr.push(value);
} else {
return;
}
});
}
setSelectedPeople(tmpArr);
};
I want to check the array for the new value by comparing it with all items. If value === item item the loop function should return, but if the value is not in the array yet, it should push it.
This is a big problem for me but I assume it is a small problem for you guys.
Use Array.every() to check if the array doesn't contain an item with the same id:
const multiSelect = value => {
const tmpArr = [...selectedPeople];
if(tmpArr.every(item => item.id !== value.id)) {
tmpArr.push(value);
}
setSelectedPeople(tmpArr);
};
However, this means that you're duplicating the array needlessly, while causing a re-render, that won't do a thing. So check if the item is already a part of selectedPeople by using Array.some(), and if it does use return to exit the function early. If it's not continue with cloning, and updating the state:
const multiSelect = value => {
if(tmpArr.some(item => item.id === value.id)) {
return;
}
const tmpArr = [...selectedPeople];
tmpArr.push(value);
setSelectedPeople(tmpArr);
};
Use find to check if the item is already in the array. Also, there's no need to make a copy of the source array:
const multiSelect = value => {
if (!selectedPeople.find(item => item.id === value.id))
setSelectedPeople(selectedPeople.concat(value))
}
Another approach.
const
multiSelect = value => setSelectedPeople([
...selectedPeople,
...selectedPeople.some(({ id }) => id === value.id)
? []
: [value]
]);

Create multidimensional javascript object from values in array using jquery .each loop

I need to create a javascript object using values stored in an array. Every value should be a new key inside the previous one. What would be the best approach to achieve this?
var option = ['level_1','level_2','level_3','level_4'];
$.each( option, function( key, value ) {
// ....
});
// I'm trying to get this result
var result = {
'level_1': {
'level_2': {
'level_3': {
'level_4':{}
}
}
}
}
You can use reduceRight for this, with the ES6 computed property name syntax.
const option = ['level_1','level_2','level_3','level_4'];
const obj = option.reduceRight( (acc, lvl) => ({ [lvl]: acc }), {});
console.log(obj);
In traditional function syntax it would be:
const obj = option.reduceRight(function (acc, lvl) {
return { [lvl]: acc };
}, {});
You have to keep track of where to put the next key. So, create a variable and initially set it to result, then on each pass through the array, move where that variable points to.
var option = ['level_1','level_2','level_3','level_4'];
var result = {};
var nextKeyGoesHere = result;
option.forEach( function( value ) {
nextKeyGoesHere[value] = {};
nextKeyGoesHere = nextKeyGoesHere[value];
});
console.log(result);
Can use Array#reduce()
var option = ['level_1','level_2','level_3','level_4'];
var res = {};
option.reduce((o, key) => (o[key] = {} , o[key]), res)
console.log(res)
you can use any of the other answers that use Array#reduce, however, if you'd like a recursive version here it is:
function _makeTree(arr, index, subtree){
if(index < arr.length){
subtree[arr[index]] = {};
return _makeTree(arr, index+1, subtree[arr[index]])
}
else return;
}
function makeTree(arr){
var tree = {};
_makeTree(arr, 0, tree)
return tree;
}
var arr = ['level_1','level_2','level_3','level_4'];
console.log(makeTree(arr));

partialRight in Lodash seems to do nothing

(I couldn't find an open Lodash Slack channel, that's why I'm posting here.)
Could you please tell me why the partialRight in this fiddle seems to do nothing? The correctRenameKeys function correctly renames the key in the supplied object, but the wrongRenameKeys function - which should do exactly the same - doesn't.
Please open the JavaScript console in your browser to see logs when running the fiddle. I tested it in Chrome.
const renameKeysOfOneObject = (object, keyMappings) => {
return _.reduce(object, function(result, value, key) {
key = keyMappings[key] || key;
result[key] = value;
return result;
}, {});
};
const correctRenameKeys = (objects, keyMappings) => {
const keysRenamer = object => renameKeysOfOneObject(object, keyMappings);
return _.map(objects, keysRenamer);
};
const wrongRenameKeys = (objects, keyMappings) => {
const keysRenamer = _.partialRight(renameKeysOfOneObject, keyMappings);
return _.map(objects, keysRenamer);
};
const object = {keyToBeRenamed: 'someValue'};
const objects = [object];
const keyMapping = {keyToBeRenamed: 'newKeyName'};
const correctlyRenamed = correctRenameKeys(objects, keyMapping);
const wronglyRenamed = wrongRenameKeys(objects, keyMapping);
console.assert(_.isEqual(correctlyRenamed, wronglyRenamed),
"The two objects should be equal. " +
"The 'keyToBeRenamed' key should have been renamed to 'newKeyName'.");
console.log(correctlyRenamed);
console.log(wronglyRenamed);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
There are two supporting statements from the lodash documentation that can answer your question:
lodash#partialRight
This method is like _.partial except that partially applied arguments
are appended to the arguments it receives.
lodash#map
Creates an array of values by running each element in collection thru
iteratee. The iteratee is invoked with three arguments: (value,
index|key, collection).
Notice that there are three arguments passed in a lodash#map iteratee, and since keyRenamer is passed as it's iteratee, then we can conclude that the invocation signature would look like this:
keyRenamer(value, index, collection, keyMappings);
If you really want to achieve the effect of having the second argument of the function renameKeysOfOneObject to be partially applied then use lodash#partial.
const wrongRenameKeys = (objects, keyMappings) => {
const keysRenamer = _.partial(renameKeysOfOneObject, _, keyMappings);
return _.map(objects, keysRenamer);
};
const renameKeysOfOneObject = (object, keyMappings) => {
return _.reduce(object, function(result, value, key) {
key = keyMappings[key] || key;
result[key] = value;
return result;
}, {});
};
const correctRenameKeys = (objects, keyMappings) => {
const keysRenamer = object => renameKeysOfOneObject(object, keyMappings);
return _.map(objects, keysRenamer);
};
const wrongRenameKeys = (objects, keyMappings) => {
const keysRenamer = _.partial(renameKeysOfOneObject, _, keyMappings);
return _.map(objects, keysRenamer);
};
const object = {keyToBeRenamed: 'someValue'};
const objects = [object];
const keyMapping = {keyToBeRenamed: 'newKeyName'};
const correctlyRenamed = correctRenameKeys(objects, keyMapping);
const wronglyRenamed = wrongRenameKeys(objects, keyMapping);
console.assert(_.isEqual(correctlyRenamed, wronglyRenamed),
"The two objects should be equal. " +
"The 'keyToBeRenamed' key should have been renamed to 'newKeyName'.");
console.log(correctlyRenamed);
console.log(wronglyRenamed);
.as-console-wrapper{min-height:100%;top:0}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>

How do you modify an object in an array and then return a new version of that array?

I'm modifying the property of an object inside an array in the following way:
const newPanoramas = state.panoramas.map(panorama => {
if (state.panorama.id === panorama.id) {
panorama.thumbnail = thumbnail
}
})
I know I have to return something---but I'm not very sure what ...
You forgot the most important part: to return the mapped object. Should be :
const newPanoramas = state.panoramas.map(panorama => {
if (state.panorama.id === panorama.id) {
panorama.thumbnail = thumbnail
}
return panorama;
});
What you have to return is the object you modified
const newPanoramas = state.panoramas.map(panorama => {
if (state.panorama.id === panorama.id) {
panorama.thumbnail = thumbnail
}
return panorama;
})
That object will be returned an stored in newPanorama.

Categories