Iterate over an entire javascript object and modify all values of type [duplicate] - javascript

This question already has answers here:
How can I access and process nested objects, arrays, or JSON?
(31 answers)
Closed 5 years ago.
I need to do a deep-iteration of a javascript object that can have nested objects and arrays and I need to execute a function on all of the numeric values and modify the object.
For example, lets say I need to multiply every number by 2.
const foo = (obj) => {
// multiply every numeric value by 2
};
const modified = foo({
a: 0,
b: 3,
c: {
d: 4,
e: {
f: 6,
g: [ 0, 3, 7, 3 ]
}
}
});
The value of modified should be:
{
a: 0,
b: 6,
c: {
d: 8,
e: {
f: 12,
g: [ 0, 6, 14, 6 ]
}
}
}
Since people typically want to know what you've tried, here's how far I got before being completely stumped.
const obj = {};
for(key in object) {
const item = object[key];
if(typeof item === 'object') {
// The levels deep is dynamic, so how would I keep doing this..
} else if(typeof item === 'array') {
obj[key] = item.map((a, b) => a * 2);
} else if(!isNaN(item)) {
obj[key] = item * 2;
}
}

Using recursion and expanding on your solution
function multiplyByTwo(objectToParse) {
const obj = {};
for (key in objectToParse) {
const item = object[key];
if (typeof item === 'object') {
obj[key] = multiplyByTwo(item);
} else if (typeof item === 'array') {
obj[key] = item.map((a, b) => a * 2);
} else if (!isNaN(item)) {
obj[key] = item * 2;
}
}
return obj;
}
const result = multiplyByTwo(object);

You probably want recursion in this case. This implementation works for any type of object and mapping function you give it, aka extremely generic
function mapper(obj, mappingFn, result) {
if (!result)
result = {};
Object.keys(obj)
.forEach(key => {
switch (typeof obj[key]) {
case 'string':
case 'number':
result[key] = mappingFn(obj[key]);
break;
// if obj[key] is an array, it still returns 'object', so we are good
case 'object':
mapper(obj[key], mappingFn, result);
break;
}
});
return result;
}
let data = {
a: 0,
b: 3,
c: {
d: 4,
e: {
f: 6,
g: [ 0, 3, 7, 3 ]
}
}
};
let result = mapper(data, value => value * 2);
console.log(result) // everything should be multiplied by 2

There's my solution.
const foo = (obj, operation) => {
let afterObj = obj;
for(item in afterObj) {
if(typeof afterObj[item] == "object") {
foo(afterObj[item], operation);
} else if (typeof afterObj[item] = "string") {
} else {
afterObj[item] = operation(afterObj[item]);
}
}
return afterObj;
};
const modified = foo({
a: 0,
b: 3,
c: {
d: 4,
e: {
f: 6,
g: [ 0, 3, 7, 3 ]
}
}
}, function(x) { return x*2 });

Related

JavaScript: how do I flatten an object like this

how do we implement a util flatten to flat this
myMap = {
  'a': 5,
  'b': 6,
  'c': {
    'f': 9,
    'g': {
      'm': 17,
      'n': 3
    }
  }
}
to
flatten(myMap) = {
  'a': 5,
  'b': 6,
‍‌‌‍‌‍‌‍‍‌‍‍‍‍‍‌‍‌‍‍ 'c.f': 9,
  'c.g.m': 17,
  'c.g.n': 3,
}
?
I found here is an implementation https://github.com/lukeed/flattie/blob/master/src/index.js but I couldn't understand the code. Can someone please give me a more readable version?
You can use this code:
myMap = {
'a': 5,
'b': 6,
'c': {
'f': 9,
'g': {
'm': 17,
'n': 3
}
}
}
function flatten(obj, pKey = null, res = {}) {
for (let key in obj) {
const name = pKey ? `${pKey}.${key}` : key;
if (typeof obj[key] === "object" && !Array.isArray(obj[key])) {
flatten(obj[key], name, res);
} else {
res[name] = obj[key];
}
}
return res;
}
console.log(flatten(myMap))
you can use it by flatten(myMap). Actually, it's simple, but sometimes it makes confusion. how it works is by looping then checking, if it is an object then calling itself again (recursive) to create a key object deeply and if not then create a key object until there.
Here is perhaps a slightly simpler version:
const inputObject = {
a: 5,
b: 6,
c: {
f: 9,
g: {
m: 17,
n: 3
}
}
}
function flatten(input, keys = [], output = {}) {
for (key in input) {
const value = input[key]
const combinedKeys = [...keys, key]
if (typeof value === 'object') {
output = flatten(value, combinedKeys, output)
} else {
output[combinedKeys.join('.')] = value
}
}
return output
}
console.log(flatten(inputObject))
/* Logs:
* {
* "a": 5,
* "b": 6,
* "c.f": 9,
* "c.g.m": 17,
* "c.g.n": 3
* }
*/
The idea is to recursively look at all properties of the object and build a .-separated key as we go deeper until we reach a leaf value.
You can read more about recursive functions here: https://www.freecodecamp.org/news/what-is-recursion-in-javascript/
Here's one option:
// Declare an object
myMap = {
'a': 5,
'b': 6,
'c': {
'f': 9,
'g': {
'm': 17,
'n': 3
}
}
}
// Declare a flatten function that takes
// object as parameter and returns the
// flatten object
const flattenObj = (myMap) => {
// The object which contains the
// final result
let result = {};
// loop through the object "ob"
for (const i in myMap) {
// We check the type of the i using
// typeof() function and recursively
// call the function again
if ((typeof myMap[i]) === 'object' && !Array.isArray(myMap[i])) {
const temp = flattenObj(myMa**strong text**p[i]);
for (const j in temp) {
// Store temp in result
result[i + '.' + j] = temp[j];
}
}
// Else store ob[i] in result directly
else {
result[i] = myMap[i];
}
}
return result;
};
console.log(flattenObj(myMap));

how to convert a js object into a dot notation string

I have a javascript plain object like this one: {a: {b: 1} }
and I want to convert it to a dot-notation string like this a.b = 1
use case:
sending the object to a plain-text environment such as cli or as a url parameter.
It's rather hard to tell whether this is what you want, but something like this would flatten a tree of objects into a list of dotted paths...
var data = {
a: {
b: 1,
c: {
d: 8
}
},
e: {
f: {
g: 9
},
h: 10,
i: [1, 2, 3]
}
};
function toDotList(obj) {
function walk(into, obj, prefix = []) {
Object.entries(obj).forEach(([key, val]) => {
if (typeof val === "object" && !Array.isArray(val)) walk(into, val, [...prefix, key]);
else into[[...prefix, key].join(".")] = val;
});
}
const out = {};
walk(out, obj);
return out;
}
console.log(toDotList(data));

Recursively iterate object and conditionally run a function

I need a function to recursively iterate an object and if the given condition is met to pass the key value to a callback function in order to get the replacement to replace the matching key. Here is a more suggestive example:
const replacement1 = { a: 1 };
const replacement2 = { b: 1 };
const getReplacement = (id) => {
if (id == 1) return replacement1;
if (id == 2) return replacement2;
};
const obj = {
foo: 2,
replaceMe: 1,
bar: {
replaceMe: 2,
},
};
function update(obj, key) {
// iterate object keys recursivelly
// if key = replaceMe then replace that key using getReplacement(obj[key])
}
console.log(update(obj, 'replaceMe'));
// should output => {foo: 2, a: 1, bar: { b: 1 } };
Sorry if the purpose for this doesn't make sense, it's just an example I could write in a hurry.
If you like to mutate the object, you could take an object for the replacements and iterate the nested objects as well.
const
update = (object, key, replacements) => {
if (key in object) {
Object.assign(object, replacements[object[key]]);
delete object[key];
}
Object.values(object).forEach(v => {
if (v && typeof v === 'object') update(v, key, replacements);
});
return object;
},
obj = { foo: 2, replaceMe: 1, bar: { replaceMe: 2 } };
console.log(update(obj, 'replaceMe', { 1: { a: 1 }, 2: { b: 1 } })); // { foo: 2, a: 1, bar: { b: 1 } };
Try like below. Explanation is in comments.
const replacement1 = { a: 1 };
const replacement2 = { b: 1 };
const getReplacement = (id) => {
if (id == 1) return replacement1;
if (id == 2) return replacement2;
};
const obj = {
foo: 2,
replaceMe: 1,
bar: {
replaceMe: 2,
},
};
function update(obj, key) {
// iterate over all key of object
Object.keys(obj).forEach(k => {
if (k == key) { // process matching key object
// get replacement object
Object.assign(obj, getReplacement(obj[key]));
// delete key property
delete obj[key];
} else if (typeof obj[k] === "object") { // Check if nested object is having key property, if yes then process that
update(obj[k], key);
}
});
return obj;
}
console.log(update(obj, 'replaceMe'));
// should output => {foo: 2, a: 1, bar: { b: 1 } };

How to merge array of objects in one object, including inner objects - JavaScript?

Suppose I have the following array of objects:
var list = [
{ a: 1,
b: { c: 'x', k: []}
},
{ a: 1,
b: {c: 'x', d: 8}
}
];
I want them to be merged into one "generic" object, for this example, it would be:
{a: 1, b: {c: 'x', d:'8', k[]}}
As you can see, all nested objects are merged too. But I can't gain it. If I use Object.assign it creates new nested objects if they are different, that is duplicates them:
var res = Object.assign({}, ...list);
// res: {
a: 1,
b: {c: 'x', k: []},
b: {c: 'x', d: 8}
}
You could try the following using the reduce method:
var list = [{
a: 1,
b: {
a: 4,
k: 3
}
}, {
a: 1,
s: 11,
b: {
ab: 4,
d: 8
}
}]
var result = list.reduce(function(acc, item) {
var obj = { ...item
}
Object.keys(obj).forEach(function(item) {
if (acc[item]) { //if a property with the the key, 'item' already exists, then append to that
Object.assign(acc[item], obj[item]);
} else { // else add the key-value pair to the accumulator object.
acc[item] = obj[item];
}
})
return acc;
}, {})
console.log(result);
Deep merging is not simple to do yourself, That blog uses deep merge.
If you don't have webpack or nodejs you can use deepmerge like so:
// see https://github.com/facebook/react/blob/b5ac963fb791d1298e7f396236383bc955f916c1/src/isomorphic/classic/element/ReactElement.js#L21-L25
var canUseSymbol = typeof Symbol === 'function' && Symbol.for
var REACT_ELEMENT_TYPE = canUseSymbol ? Symbol.for('react.element') : 0xeac7
function isReactElement(value) {
return value.$$typeof === REACT_ELEMENT_TYPE
}
function isNonNullObject(value) {
return !!value && typeof value === 'object'
}
function isSpecial(value) {
var stringValue = Object.prototype.toString.call(value)
return stringValue === '[object RegExp]'
|| stringValue === '[object Date]'
|| isReactElement(value)
}
function defaultIsMergeableObject(value) {
return isNonNullObject(value)
&& !isSpecial(value)
}
function emptyTarget(val) {
return Array.isArray(val) ? [] : {}
}
function cloneUnlessOtherwiseSpecified(value, options) {
return (options.clone !== false && options.isMergeableObject(value))
? deepmerge(emptyTarget(value), value, options)
: value
}
function defaultArrayMerge(target, source, options) {
return target.concat(source).map(function(element) {
return cloneUnlessOtherwiseSpecified(element, options)
})
}
function mergeObject(target, source, options) {
var destination = {}
if (options.isMergeableObject(target)) {
Object.keys(target).forEach(function(key) {
destination[key] = cloneUnlessOtherwiseSpecified(target[key], options)
})
}
Object.keys(source).forEach(function(key) {
if (!options.isMergeableObject(source[key]) || !target[key]) {
destination[key] = cloneUnlessOtherwiseSpecified(source[key], options)
} else {
destination[key] = deepmerge(target[key], source[key], options)
}
})
return destination
}
function deepmerge(target, source, options) {
options = options || {}
options.arrayMerge = options.arrayMerge || defaultArrayMerge
options.isMergeableObject = options.isMergeableObject || defaultIsMergeableObject
var sourceIsArray = Array.isArray(source)
var targetIsArray = Array.isArray(target)
var sourceAndTargetTypesMatch = sourceIsArray === targetIsArray
if (!sourceAndTargetTypesMatch) {
return cloneUnlessOtherwiseSpecified(source, options)
} else if (sourceIsArray) {
return options.arrayMerge(target, source, options)
} else {
return mergeObject(target, source, options)
}
}
deepmerge.all = function deepmergeAll(array, options) {
if (!Array.isArray(array)) {
throw new Error('first argument should be an array')
}
return array.reduce(function(prev, next) {
return deepmerge(prev, next, options)
}, {})
}
var list = [{
a: 1,
b: {
c: 'x',
//merging 1,2 and 1,3 results in [1,2,1,3] you can change that in defaultArrayMerge
k: [1,2]
}
},
{
a: 1,
b: {
c: 'x',
k: [1,3],
d: 8
}
}];
console.log(
deepmerge.all(list)
)
You can use the reduce method. Remove the first element from the original list , that object will be the base method.
var list = [{
a: 1,
b: {
c: 'x',
k: []
}
},
{
a: 1,
b: {
c: 'x',
d: 8
}
}
];
// Remove the first element from the array. The first element will be
// the base object
// slice will return a new array without the first object
// apply reduce on this list
let _temp = list.slice(1);
let x = _temp.reduce(function(acc,curr,currIndex){
for(let keys in curr){
// checking if the base object have the same key as of current object
if(acc.hasOwnProperty(keys)){
// if base object and current object has the key then
// check if the type is an object
if(typeof curr[keys] ==='object'){
// now get the key from both the object
// & check which one is missong. Add that key and value to the
// base object
let keysFromACC = Object.keys(acc[keys]);
let keysFromCURR = Object.keys(curr[keys]);
keysFromCURR.forEach(function(item){
if(keysFromACC.indexOf(item) ===-1){
acc[keys][item] = curr[keys][item]
}
})
}
}
else{
// if the base object does not have key which current object
// has then add the key to base object
acc[keys]= curr[keys]
}
}
return acc;
},list[0]);
console.log(x)

Compare 2 objects and remove repeating keys between

I am experimenting on objects, and what I am trying to achieve is to remove keys found in object1 if those keys exist in object2.
Here is the example:
var original = {
a: 1,
b: 2,
c: 3,
e: {
tester: 0,
combination: {
0: 1
}
},
0: {
test: "0",
2: "hello"
}
};
var badKeys = {
a: 1,
b: 2,
0: {
test: "0",
}
}
var expectedResult = {
c: 3,
e: {
tester: 0,
combination: {
0: 1
}
},
0: {
2: "hello"
}
}
I've tried using underscore difference function, but it doesn't work for objects, also not sure if this is the right function.
Can you help me to get the var expectedResult right?
You could use an iterative and recursive approach for geeting the wanted properties in a new object.
function deleteKeys(good, bad, result) {
Object.keys(good).forEach(function (key) {
if (bad[key] && typeof bad[key] === 'object') {
result[key] = {};
deleteKeys(good[key], bad[key], result[key]);
return;
}
if (!(key in bad) || good[key] !== bad[key]) {
result[key] = good[key];
}
});
}
var original = { a: 1, b: 2, c: 3, e: { tester: 0, combination: { 0: 1 } }, 0: { test: "0", 2: "hello", another: { a: { B: 2, C: { a: 3 } }, b: 2 } } },
badKeys = { a: 1, b: 2, 0: { test: "0", random: 2, another: { a: 1 } } },
result = {};
deleteKeys(original, badKeys, result);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
This would be the algorithm:
function removeDifferences (original, removeKeys) {
// Get keys of to be deleted properties.
var keys = Object.keys(removeKeys);
// Iterate all properties on removeKeys.
for (key of keys) {
// Check if property exists on original.
if (typeof original[key] !== undefined) {
// If the property is an object, call same function to remove properties.
if (typeof removeKeys[key] === 'object') {
removeDifferences(original[key], removeKeys[key]);
} else {
delete original[key];
}
}
}
return original;
}
Applied to your case:
/* Your data. */
var original = {
a: 1,
b: 2,
c: 3,
e: {
tester: 0,
combination: {
0: 1
}
},
0: {
test: "0",
2: "hello"
}
};
var badKeys = {
a: 1,
b: 2,
0: {
test: "0",
}
};
var expectedResult = {
c: 3,
e: {
tester: 0,
combination: {
0: 1
}
},
0: {
2: "hello"
}
};
/* Function */
function removeDifferences(original, removeKeys) {
// Get keys of to be deleted properties.
var keys = Object.keys(removeKeys);
// Iterate all properties on removeKeys.
for (key of keys) {
// Check if property exists on original.
if (typeof original[key] !== undefined) {
// If the property is an object, call same function to remove properties.
if (typeof removeKeys[key] === 'object') {
removeDifferences(original[key], removeKeys[key]);
} else {
delete original[key];
}
}
}
return original;
}
/* Application */
var output = removeDifferences(original, badKeys);
console.log(output);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can create recursive function that will return new object using for...in loop.
var original = {"0":{"2":"hello","test":"0"},"a":1,"b":2,"c":3,"e":{"tester":0,"combination":{"0":1}}}
var badKeys = {"0":{"test":"0"},"a":1,"b":2}
function remove(o1, o2) {
var result = {}
for (var i in o1) {
if (!o2[i]) result[i] = o1[i]
else if (o2[i]) {
if (typeof o1[i] == 'object' && typeof o2[i] == 'object') {
result[i] = Object.assign(result[i] || {}, remove(o1[i], o2[i]))
} else if (o1[i] != o2[i]) result[i] = o1[i]
}
}
return result
}
console.log(remove(original, badKeys))
Truly a job for some recursion and a bit of functional programming using a pure function. (Tested with Node v7.7.1)
"DoForAllNestedObjects" for applying some function "whattodo" on "every leaf on the dictionary tree" when there is an corresponding "leaf" in baddict.
let DoForAllNestedValues = (dict, baddict, whattodo) => {
for (let key in dict) {
if (typeof (dict[key]) === 'object' && typeof (baddict[key]) === 'object')
DoForAllNestedValues(dict[key], baddict[key], whattodo);
else
if (baddict[key])
whattodo(dict, key);
}
}
DoForAllNestedValues(original, badKeys, (obj, val) => delete obj[val]);
console.log(original);

Categories