how to convert a js object into a dot notation string - javascript

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

Related

Replace all the nested object properties to particular value [duplicate]

This question already has answers here:
Replace nested field values in an object
(5 answers)
Closed 5 months ago.
Write a function that should take an array of objects and it should replace a particular value of the key?
for example: in below object, all the value of "bar" should be replaced to "foo-bar"
let obj = {
a: "foo",
b: "bar",
c: {
d: "foo",
e: "bar",
f: {
g: "test",
h: "bar",
}
}
}
Note: All the nested object properties also needs to be replaced
Recursively loop through all the keys.
const obj = {
a: 'foo',
b: 'bar',
c: {
d: 'foo',
e: 'bar',
f: {
g: 'test',
h: 'bar',
},
},
};
function replaceValuesInObj(o, targetValue, replaceValue) {
Object.keys(o).forEach((key) => { // forEach key in the object
if (o[key] === targetValue) o[key] = replaceValue; // is the value your condition? set it to 'foo-bar'
if (typeof o[key] === 'object' && o[key] !== null) { // if the key is an object (call the function recursively)
replaceValuesInObj(o[key], targetValue, replaceValue);
}
});
}
replaceValuesInObj(obj, 'bar', 'foo-bar');
console.log(obj);

Edge cases for deep copy of an object in Javascript?

I'm trying to make a deep copy of an object without using lodash.
I'm unable to think of any case where this will fail. Can someone suggest me any edge case where this may fail?
let Obj = {
a: "Hello",
b: {
c: {
h: "World"
},
d: null
},
e: () => 'me',
f: new Date(),
g: [1, 2, 3]
}
let result = JSON.parse(JSON.stringify(Obj));
for (let key in Obj) {
if (!result.hasOwnProperty(key) || Obj[key] instanceof Date) {
result[key] = Obj[key];
}
}
console.log(result)

How to merge two objects, overriding null values?

I'd like to merge two similar but not identical objects and override null values in one of them, if such exist. For example I'd have these two objects:
const obj1 = {
a: 1,
b: '',
c: [],
d: null
}
const obj2 = {
a: 2,
b: null,
d: 1
}
And the effect of merge should be:
const objMerged = {
a: 2,
b: '',
c: [],
d: 1
}
In other words, the most important source of data in the merged object is obj2 but it lacks some properties from obj1, so they need to be copied and also some of the obj2 values are null so they should be taken from obj1 as well.
EDIT
I tried:
_.extend({}, obj1, obj2)
and
Object.assign({}, obj1, obj2)
You could also mix and match with ES6 destructuring and lodash _.omitBy:
const obj1 = { a: 1, b: '', c: [], d: null }
const obj2 = { a: 2, b: null, d: 1 }
const result = {..._.omitBy(obj1, _.isNull), ..._.omitBy(obj2, _.isNull)}
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
You could also do it with ES6 only like this:
const obj1 = { a: 1, b: '', c: [], d: null }
const obj2 = { a: 2, b: null, d: 1 }
let omitNull = obj => {
Object.keys(obj).filter(k => obj[k] === null).forEach(k => delete(obj[k]))
return obj
}
const result = { ...omitNull(obj1), ...omitNull(obj2) }
console.log(result)
To add to this list of good answers, here's a recursive solution that will work with nested structures.
This example will merge the common properties of the dst object to the src object in all levels of nesting, leaving any properties that are not common intact.
const merge = (dst, src) => {
Object.keys(src).forEach((key) => {
if (!dst[key]) {
dst[key] = src[key];
} else if (typeof src[key] === 'object' && src[key] !== null && typeof dst[key] === 'object' && dst[key] !== null) {
merge(dst[key], src[key]);
}
});
},
/* Usage: */
src = {
prop1: '1',
prop2: {
val: 2,
}
},
dst = {
prop1: null,
prop2: {
val: null,
},
prop3: null,
};
merge(dst, src);
console.log(dst);
You can use _.mergeWith(), and in the merge callback only take the 2nd value if it's not null:
const obj1 = { a: 1, b: '', c: [], d: null }
const obj2 = { a: 2, b: null, d: 1 }
const result = _.mergeWith({}, obj1, obj2, (o, s) => _.isNull(s) ? o : s)
console.log(result)
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.11/lodash.min.js"></script>
Here is a pure JS based solution:
Iterate through the first object to replace values from second object, then add the additional values from the second object.
const obj1 = {
a: 1,
b: '',
c: [],
d: null
}
const obj2 = {
a: 2,
b: null,
d: 1
}
function mergeObjs(obj1, obj2){
const merged = {}
keys1 = Object.keys(obj1);
keys1.forEach(k1 => {
merged[k1] = obj2[k1] || obj1[k1]; // replace values from 2nd object, if any
})
Object.keys(obj2).forEach(k2 => {
if (!keys1.includes(k2)) merged[k2] = obj[k2]; // add additional properties from second object, if any
})
return merged
}
console.log(mergeObjs(obj1, obj2))
Using Lodash by create() and omitBy()
const obj1 = {"a":1,"b":"","c":[],"d":null}
const obj2 = {"a":2,"b":null,"d":1}
const objMerged = _.create(
_.omitBy(obj1, _.isNull),
_.omitBy(obj2, _.isNull)
)
console.log(objMerged)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
If you're interested in only the first level of the two objects you could do something like this:
const obj1 = {
a: 1,
b: '',
c: [],
d: null
}
const obj2 = {
a: 2,
b: null,
d: 1
}
const merged = Object.keys(obj1).concat(Object.keys(obj2)) // create an array that contains the keys of the two objects.
.filter((k, i, arr) => arr.indexOf(k) === i) // remove duplicate keys
.reduce((a, c) => {
a[c] = obj1[c] !== null ? obj1[c] : obj2[c];
return a;
}, {});
console.log(merged);
This example only check for null values, you should probably extend it to check for others like undefined, empty strings, etc.
You did it the good way using Object.assign, just remove what you don't want right before
Object.keys(obj1).forEach( k => {
if ( obj1[k] //write the condition you want
delete obj1[k]
});
var objMerged = {};
for (var kobj1 in obj1) {
for (var kobj2 in obj2) {
if (obj1[kobj1] == null && obj2[kobj1] != null)
objMerged[kobj1] = obj2[kobj1];
else if (obj2[kobj2] == null && obj1[kobj2] != null)
objMerged[kobj2] = obj1[kobj2];
}
}
//Print objMerged to display

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)

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

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

Categories