How to change key name with map into nested object? - javascript

Here is the object
{
a: 1,
b: {
c: {
d: 2
},
e: 3
}
}
Here is the map
{
'a': 'aaa',
'b': 'bbb',
'b.c.d': 'bcd'
}
Here is the expected result.
{
aaa: 1,
bbb: {
c: {
bcd: 2
},
e: 3
}
}
I know there's a function in lodash _.get could get the value like b.c.d.
But how can I change the key name with the map?

You can do this recursively by keeping track of the current path and building a key into the map with that:
let o = {a: 1,b: {c: {d: 2},e: 3}}
let map = {
'a': 'aaa',
'b': 'bbb',
'b.c.d': 'bcd'
}
function makeObj(obj, map, p=[]) {
let ret = {}
Object.entries(obj).forEach(([k, v]) => {
let path = p.concat(k) // add onto current path
let mapKey = map[path.join('.')] || k
ret[mapKey] = (typeof v === 'object')
? makeObj(v, map, path) // if an object recurse and pass on the current path
: v // otherwise set the value
})
return ret
}
console.log(makeObj(o, map))

Related

How to expand single level nested hashes on the fly?

I have an object where a few of the keys are nested single level hashes. In this example only b is nested.
const j = {
a: 'A',
b: {
bb: 'BB',
bbb: 'BBB',
},
c: 'C'
};
Question
What I am looking for is a way to loop over the object and if a key is a nested object, then print its keys instead.
a
bb
bbb
c
Does anyone know how to do that?
You can do this recursively:
function printKeys(obj) {
for (const [key, val] of Object.entries(obj)) {
if (typeof val === "object") {
printKeys(val);
} else {
console.log(key);
}
}
}
If you only have one level of nesting at most, #blex's answer is probably the better one.
You could do it with Object.entries and flatMap:
const j = { a: 'A', b: { bb: 'BB', bbb: 'BBB' }, c: 'C' };
function getOneLevelKeys(obj) {
return Object.entries(obj)
.flatMap(([key, value]) => typeof value === "object" ? Object.keys(value) : key);
}
console.log( getOneLevelKeys(j) );
You can use a recursive flatMap with Object.keys.
const j = {
a: 'A',
b: {
bb: 'BB',
bbb: 'BBB',
},
c: 'C'
};
const getKeys = o => Object.keys(o).flatMap(x => o[x] === Object(o[x])
? getKeys(o[x]) : x);
console.log(getKeys(j));

How would you check if property name(s) from one object exist in another?

I am trying to transfer/copy properties from object1 to object2, but only properties that are undefined in object2.
Thanks in advance, I hope this comes across clearly!
let object1 = { a: 1, b: 2, c: 3, d: 4 }
let object2 = { a: 'string' }
fillObj = function (object2, object1) {
for (let key in object1) {
if (typeof object2.key === undefined) {
object2[key] = object1[key];
}
}
return object2; //should return {a: 'string', b: 2, c: 3, d: 4 }
};
(1) Look properties on an object by a variable property name by using bracket notation, not dot notation
(2) To check if something is undefined, either compare against undefined directly, or use typeof and compare against the string 'undefined' (but this check isn't needed for this algorithm)
(3) Make sure the properties are own properties, not inherited properties, with hasOwnProperty
let object1 = { a: 'string' }
let object2 = { a: 1, b: 2, c: 3, d: 4 }
fillObj = function (object2, object1) {
for (let key in object1) {
if (object1.hasOwnProperty(key)) {
object2[key] = object1[key];
}
}
return object2; //should return {a: 'string', b: 2, c: 3, d: 4 }
};
console.log(fillObj(object2, object1));
Or use Object.entries, which iterates over own-properties only:
let object1 = { a: 'string' }
let object2 = { a: 1, b: 2, c: 3, d: 4 }
fillObj = function (object2, object1) {
for (const [key, val] of Object.entries(object1)) {
object2[key] = val;
}
return object2; //should return {a: 'string', b: 2, c: 3, d: 4 }
};
console.log(fillObj(object2, object1));

Add object and array for same properties

var obj = {x:{y: {a: 1, b:2}}, p: 11}
var arr = [{x: {y: {c: 3}}},{x: {y: {d: 4}}}]
it can be done by lodash merge(obj, ...arr)
but I don't want to use lodash merge method
outputObj = {x:{y: {a: 1, b:2, c: 3, d: 4}}, p: 11}
You could take an iterative and recursive approach and check the type of the value and take either an array or object if a parent property is not given.
function merge(target, ...source) {
source.forEach(s => Object.entries(s).forEach(([k, v]) => {
if (v && typeof v === 'object') {
merge(target[k] = target[k] || (Array.isArray(v) ? [] : {}), v);
} else {
target[k] = v;
}
}));
}
var obj = { x: { y: { a: 1, b: 2 } }, p: 11 },
arr = [{ x: { y: { c: 3 } } }, { x: { y: { d: 4 } } }]
merge(obj, ...arr)
console.log(obj);
I found you can do this in "one line" using recursive reduction
const
merge = (target, obj) =>
Object.keys (obj).reduce((merged, key) => ({
...merged,
[key]:[obj[key]].reduce(merge, target[key])||obj[key]
}), target),
merged = arr.reduce (merge,obj);
console.log (merged);
<script>
var obj = {x:{y: {a: 1, b:2}}, p: 11}
var arr = [{x: {y: {c: 3}}},{x: {y: {d: 4}}}]
</script>

Access a property on an object from an array of keys

I want to write a helper function to unpack a specific object property from each object in an array of objects. Sometimes this property will be top level, other times it will be nested an arbitrary number of levels. So the crux of this question is: how can I access an object property based on an array of key names of variable length?
I'm hoping for something like:
const func = (arrOfObjects, ...keys) {
return arrOfObjects.map(object => {
return object[keys[0]][keys[1]] ... [keys[N]];
})
}
with example behaviour:
const input = [
{a: b: {c: 10}},
{a: b: {c: 11}},
{a: b: {c: 12}}
]
console.log(func(input, 'a', 'b', 'c'))
// [10, 11, 12]
console.log(func(input, 'a', 'b'))
// [{c: 10}, {c: 11}, {c : 12}]
I feel like there has to be a nice ES6 wizardry solution but as yet haven't found it so any help would be much appreciated!
Cheers,
P
You can get a short and easy solution using Array#reduce
const input = [
{a: { b: {c: 10}}},
{a: { b: {c: 11}}},
{a: { b: {c: 12}}}
]
console.log(func(input, ['a', 'b', 'c']))
// [10, 11, 12]
console.log(func(input, ['a', 'b']))
// [{c: 10}, {c: 11}, {c : 12}]
function func(input, props) {
return input.map(x => exctractByProps(x, props));
}
function exctractByProps(obj, props) {
return props.reduce(
(acc, prop) => typeof acc === 'object' && prop in acc ? acc[prop] : undefined,
obj
)
}
The main logic is to grab all the properties passed in and then try to get the value corresponding to obj[prop[0]][prop[1]][prop[2]]/* ... */[prop[n]]. If the object has an odd shape that doesn't match up with prop (for example, an input of {a: 1}, ['a', 'b'] or {d: {c: 1}}, ['a', 'b']) then the function returns undefined.
Based on the answers you gave me to my questions and your example. It seems as if the order will of the input will always match the objects nesting. So here is my solution:
const func = (arrOfObjects, ...keys) => {
return arrOfObjects.map(object => {
let obj = object, integer = keys.length;
for (let index = 0; index < integer; index++) {
obj = obj[keys[index]];
if(obj === undefined) break;
}
return obj;
});
};
const input = [
{ a: { b: { c: 10 } } },
{ a: { b: { c: 11 } } },
{ a: { b: { c: 12 } } }
];
console.log(func(input, "a", "b", "c"));
// [10, 11, 12]
console.log(func(input, "a", "b"));
// [{c: 10}, {c: 11}, {c : 12}]
Unfortunately there is no such thing as the javascript magic you where expecting.
Note: this code will not work when the order of the keys inside the object are nested at random depth. But for what you are trying to solve, this should work just fine. Also, I tried to preserve your initial code as good as possible
If you supply the accessor like [a, b, c[0], name], you can write a custom function which returns the value if found in a nested Object, otherwise returns undefined
let obj = {
a: 1,
b: 2,
c: {
d: 1,
e: [{
f: 3,
g: [{
z: 45
}]
}]
}
}
function findKeyFromPattern(obj, patternArr) {
let value = obj;
for(let i = 0; i < patternArr.length; i++) {
const arrmatch = patternArr[i].match(/(\w+)\[(\d+)\]/);
if(arrmatch) { // pattern matches and array accessor syntax
value = value[arrmatch[1]];
if(typeof value === 'object' && value !== null) {
value = value[arrmatch[2]];
} else {
return;
}
} else {
if(value[patternArr[i]]) {
value = value[patternArr[i]];
} else {
return;
}
}
}
return value;
}
console.log(findKeyFromPattern(obj, ['c', 'e[0]', 'g']));
console.log(findKeyFromPattern(obj, ['c', 'e[1]', 'g']))

Hot to use reduce to convert a array of objects to a single object

i'm trying to work with reduce or map, but i'm a noob some times.
i'm trying to use this function to return a single array from the objects.
var obj = [{ a: 1 }, { b: 2 }, { c: 3 }];
var result = obj.reduce((obj, item) => [item.key] = item.value);
console.log(result);
but i'm always getting :
Uncaught TypeError: Cannot read property 'Symbol(Symbol.iterator)'
of undefined
I searched a lot, but the examples didn't help me... i think that's something simple, but after 1 hour, i'm nothing getting .
What i want..
[{a: 1}, {b: 2}, {c: 3}] to {a: 1, b: 2, c: 3}
You could use Object.assign and spread syntax ....
var obj = [{ a: 1 }, { b: 2 }, { c: 3 }];
console.log(Object.assign({}, ...obj));
With Array#reduce
var obj = [{ a: 1 }, { b: 2 }, { c: 3 }];
console.log(obj.reduce((r, o) => Object.assign(r, o), {}));
Without Object.assign
var obj = [{ a: 1 }, { b: 2 }, { c: 3 }];
console.log(obj.reduce((r, o) => (Object.entries(o).forEach(([k, v]) => r[k] = v), r), {}));
ES5
var obj = [{ a: 1 }, { b: 2 }, { c: 3 }];
console.log(obj.reduce(function (r, o) {
Object.keys(o).forEach(function (k) {
r[k] = o[k];
});
return r;
}, {}));
If you want to use reduce:
var arr = [{ a: 1 }, { b: 2 }, { c: 3 }];
var result = arr.reduce((obj, item) => Object.assign(obj, item), {});
Check the MDN documentation when in doubt.
you can do it in the following way using reduce
var obj = [{ a: 1 }, { b: 2 }, { c: 3 }];
var result = obj.reduce((obj, item) => {
Object.assign(obj, item)
return obj;
}, {});
console.log(result);

Categories