How to reverse key order deep on JavaScript object - javascript

I need to deep reverse the order of all keys in a JavaScript object.
For example:
{
a: 'val1',
b: 'val2',
c: {
ca: 'val3',
cb: 'val4'
}
}
Must become:
{
c: {
cb: 'val4',
ca: 'val3'
},
b: 'val2',
a: 'val1'
}
I have tried writing a recursive function, but I cannot figure out how to fix it:
function reverseKeys (obj, newObj = {}) {
Object.keys(obj).reverse().forEach(key => {
if(typeof obj[key] === 'object') {
newObj = reverseKeys(obj[key], {})
}
newObj[key] = obj[key]
})
return newObj
}
Solution inspired by #Unmitigated's input
function reverseKeys (obj) {
return Object.fromEntries(
Object.entries(obj)
.reverse()
.map(
([k, v]) => {
if (v.constructor === Object && v.constructor !== Array && v !== null) {
return [k, reverseKeys(v)]
} else if (v.constructor === Array && v !== null) {
const a = []
v.forEach(elem => {
if ((elem.constructor === Object || elem.constructor === Array) && elem !== null) {
a.push(reverseKeys(elem))
} else {
a.push(elem)
}
})
return [k, a]
} else {
return [k, v]
}
}
)
)
}

You can map over Object.entries recursively.
let o = {
a: 'val1',
b: 'val2',
c: {
ca: 'val3',
cb: 'val4'
},
d: [{a : 1, b : 2}, {c: 3}]
};
function reverseKeys(obj) {
if (Array.isArray(obj)) return [...obj].reverse().map(reverseKeys);
if (Object.getPrototypeOf(obj) === Object.prototype)
return Object.fromEntries(Object.entries(obj).reverse()
.map(([k, v]) => [k, reverseKeys(v)]));
return obj;
}
console.log(reverseKeys(o));

Related

create variables from an object

I'm working on a project where I get an object in input like this one :
const obj = {
a: 'somestring',
b: 42,
c: {
d: 'foo',
e: 'bar'
},
f: [1, 2]
};
and I need to create some variables to get to this output :
const a = "somestring"
const b = 42
const c.d = "foo"
const c.e = "bar"
const f[0] = 1
const f[1] = 2
I got a result with this code :
for (const [k1, v1] of Object.entries(obj)) {
if (typeof v1 === "object") {
if (Array.isArray(v1)) {
for (const [k2, v2] of Object.entries(v1)) {
console.log(`const ${k1}[${k2}] = ${v2}`);
}
} else {
for (const [k2, v2] of Object.entries(v1)) {
console.log(`const ${k1}.${k2} = ${v2}`);
}
}
} else {
console.log(`const ${k1} = ${v1}`);
}
}
But when I get an object more complex like this one :
const obj = {
a: [
{
b: 'lorem'
},
{
c: 'ipsum'
}
],
d: {
e: {
f : 'foobar'
}
}
};
My output look like this :
const a[0] = [object Object]
const a[1] = [object Object]
const d.e = [object Object]
I can't find any relevant solutions. Is there a solution or npm package for this?
There is a feature called destructuring docs click here
This feature as the docs say will help you create new variables from a nested object.
Taking a object which is nested you can access its leaf values and assign them directly to vars like this:
const o = {
a: 'a',
b: {
c : 'c',
d: {
e: 'e'
}
}
};
const {a ,b : { c, d: {e} }} = o;
alert(a);
alert(c);
alert(e);
Edit: it works with arrays as well not objects only
You can do it with eval function in JS:
const obj = {
a: 'somestring',
b: 42,
c: {
d: 'foo',
e: 'bar'
},
f: [1, 2]
};
var log = console.log;
for (let key in obj) {
//log(key);
eval(`var ${key} = obj.${key}`);
}
log(a);
log(b);
log(c);
log(f);
I think you want something like this:
const obj = {
a: 'somestring',
b: 42,
c: {
d: 'foo',
e: 'bar'
},
f: [1, 2]
};
const parse = (obj, prefix = '', isArray=false) => {
Object.entries(obj).forEach(([k, v]) => {
if (typeof v === 'object') parse(v, `${k}`, Array.isArray(v))
else {
const value = (typeof v === 'string') ? `"${v}"` : v;
const before = isArray ? '[' : prefix ? '.' : '';
const after = isArray ? ']' : '';
console.log(`const ${prefix}${before}${k}${after} = ${value}`)
}
});
}
parse(obj);

Deleting key to nested object?

In this question I got some excellent answers, but it turned out I had over simplified the problem I am facing.
I have the same object
const obj = {
a: 'A',
b: {
bb: 'BB',
bbb: 'BBB',
},
c: 'C'
};
and I somehow need to end up with
{ a: 'A', bb: 'BB', bbb: 'BBB', c: 'C' }
where there key to the nested object is removed. It doesn't have to be in place. Creating a new object is fine.
Question
Can anyone figure out how to delete the key from the nested object, but still keep the nested object?
You can flatten it recursively:
function flattenObject(obj) {
const ret = {};
for (const [key, val] of Object.entries(obj)) {
if (typeof val === "object") {
Object.assign(ret, flattenObject(val));
} else {
ret[key] = val;
}
}
return ret;
}
Get an array of [key, value] pairs using Object.entries(). Iterate the pairs with Array.flatMap(). If the value is object, call the function with the value. If not return an object of { [key]: value }. Merge to a single object by spreading the array of object into Object.assign().
Use recursion and merge the sub objects by spreading into Object.assign():
const fn = obj => Object.assign({},
...Object.entries(obj)
.flatMap(([k, v]) => typeof(v) === "object" ? fn(v) : { [k]: v })
);
const j = { a: 'A', b: { bb: 'BB', bbb: 'BBB' }, c: 'C' };
const result = fn(j);
console.log(result);
Another option is to work directly with the [key, value] entries (p), and then convert everything to a single object using Object.fromEntries():
const fn = obj =>
Object.entries(obj)
.flatMap(p => typeof(p[1]) === "object" ? fn(p[1]) : [p])
const j = { a: 'A', b: { bb: 'BB', bbb: 'BBB' }, c: 'C' };
const result = Object.fromEntries(fn(j));
console.log(result);

how to sort array in node js

I have below structure of array.
{
"a": "aa",
"**b**": {
"**b**": "bb",
"c": 1
},
"d": "d"
},
I want to display the end result like below.
{ "a": "aa",
"b": "bb",
"c": 1
"d": "dd"
},
I am trying with below code but its not working as expected.
let finalArr = [];
for (let [key, value] of Object.entries(resObj)) {
if (typeof value === 'object') {
for (let [keyInternal, valueInternal] of Object.entries(value)) {
valueInternal.map(arrValue => {
const finalObj = {
a: '',
b: '',
c : '',
d : ''
};
finalObj.a = key;
finalObj.b = arrValue[1].b;
finalObj.c = arrValue[1].c;
finalObj.d = keyInternal;
finalArr.push(finalObj);
});
}
}
}
You can use recursion to flatten you object something like this
let obj = {
"a": "aa",
"**b**": {
"**b**": "bb",
"c": 1,
},
"d": "d",
"*e*": {
"**e**": {
"e": 2
}
}
}
let flatten = (obj, final = {}) => {
Object.entries(obj).forEach(([key, value]) => {
if (typeof value === 'object') {
flatten(value, final)
} else {
final[key] = value
}
})
return final
}
console.log(flatten(obj))
Use forEach on entries and build new object. if the value is object then use Object.assign to flatten.
const obj = {
a: "aa",
b: {
b: "bb",
c: 1
},
d: "d"
};
const flatten = obj => {
const res = {};
Object.entries(obj).forEach(([key, value]) => {
if (typeof value === "object") {
Object.assign(res, value);
} else {
res[key] = value;
}
});
return res;
};
console.log(flatten(obj));
I have solved it using below way.
let finalArr = [];
for (let [key, value] of Object.entries(resObj)) {
if (typeof value === 'object') {
for (let [keyInternal, valueInternal] of Object.entries(value)) {
valueInternal.map(arrValue => {
const finalObj = {
a: '',
d: ''
};
finalObj.a= key;
finalObj.d= keyInternal;
var tempobj = {...finalObj,...arrValue};
finalArr.push(tempobj);
});
}
}
}

update nested json object using recursion in JavaScript

I'm trying to create an updated object from an existing object.
The sample object is:
// sample object
const testObj = {
a: 1,
b: {
c: 2,
d: {
e: 3,
f: {
g: 4
}
}
}
};
I want to create a new object from the above object with some concatenation of each value:
// expected object
const expectedObject= {
a: '1 a',
b: {
c: '2 a',
d: {
e: '3 a',
f: {
g: '4 a'
}
}
}
};
here is my sample code:
let expectedObject = {};
const newObject = object => {
Object.entries(object).forEach(([key, value]) => {
if (typeof value === "object") {
Object.keys(value).map(key => {
value[key] = value[key] + " a";
return value;
});
expectedObject[key] = value;
//return newTest;
} else {
expectedObject[key] = value;
return expectedObject;
}
});
return expectedObject;
};
console.log(newObject(testObj));
the outcome in console is:
{a: 1, b: {…}}
a: 1
b:
c: "2 a"
d: "[object Object] a"
__proto__: Object
__proto__: Object
I wanted to use recursion here and also tried it but no luck.
any help, thanks?
You could get a new object my mapping changed values and creating new objects.
function map(object, fn) {
return Object.fromEntries(Object
.entries(object)
.map(([k, v]) => [k, v && typeof v === 'object' ? map(v, fn) : fn(v)])
);
}
var object = { a: 1, b: { c: 2, d: { e: 3, f: { g: 4 } } } },
result = map(object, v => v + ' a');
console.log(result);
If you have arrays inside, you could add a check in advance and map the values.
const
map = fn => {
const iter = v => v && typeof v === 'object'
? Array.isArray(v)
? v.map(iter)
: Object.fromEntries(Object.entries(v).map(([k, v]) => [k, iter(v, fn)]))
: fn(v);
return iter;
};
var object = { a: 1, b: { c: 2, d: { e: 3, f: { g: 4, k: [5, 6] } } } },
addA = map(v => v + ' a'),
result = addA(object);
console.log(result);
This is simply a refactoring of the answer from #user633183. I like that approach a lot, but think it can be simplified by extracting two more reusable functions. This started as a comment on that answer, but I thought it would be better to be explicit.
const map = (f) => (a) =>
a.map(f)
const mapObj = (f) => (o) =>
Object .entries (o) .reduce ( (a, [k, v] ) => ({ ...a, [k]: f(v) }), {})
const traverse = (f) => (t) =>
Array.isArray(t)
? map (traverse (f)) (t)
: Object(t) === t
? mapObj (traverse (f)) (t)
: f (t)
const input =
{ a: [ 1, 11, 111 ], b: { c: 2, d: { e: [ 3, { f: { g: 4 } } ] } } }
const output =
traverse(x => `${x} a`) (input)
console.log(output)
mapObj can be written in many different ways. Here are two alternatives:
const mapObj = (f = identity) => (o = {}) =>
Object .fromEntries (Object .entries (o) .map (([ k, v ]) => [ k, f (v) ]))
const mapObj = (f = identity) => (o = {}) =>
Object .assign .apply (null, Object .entries (o) .map (([ k, v ]) => ({ [k]: f (v)
Here's an approach using a modification of the original code to demonstrate what needed to be changed in order to make it work. You had some things switched up reading the value and setting the new one. Also I'm using the spread operator to clone the object before modifying it.
const testObj = {
a: 1,
b: {
c: 2,
d: {
e: 3,
f: {
g: 4
}
}
}
};
const newObject = object => {
const clonedObj = { ...object };
const entries = Object.entries(clonedObj);
entries.forEach(([key, value]) => {
if (typeof value === "object") {
clonedObj[key] = newObject(value);
} else {
clonedObj[key] = value + " a";
}
});
return clonedObj;
};
console.log(newObject(testObj));
console.log(testObj); // prove that the original object hasn't changed
Here's a simple recursive technique. It is similar to Nina's but it preserves arrays, if present in the structure.
If the input, t, is an array, create a new array by traversing each array value, v, with the traversing function, f
(inductive) Otherwise t is not an array. If t is an object, create a new object from key value pairs, [ k, v ], by traversing each value, v, with the traversing function, f
(inductive) Otherwise t is not an array and t is not an object. This means t is either a primitive value, such as string, number, or null
Numbered comments below correspond to the explanation above -
const identity = x =>
x
const traverse = (f = identity, t = {}) =>
Array.isArray(t) // 1
? Array.from(t, v => traverse(f, v))
: Object(t) === t // 2
? Object.fromEntries(Object.entries(t).map(([ k, v ]) => [ k, traverse(f, v) ]))
: f (t) // 3
const input =
{ a: [ 1, 11, 111 ], b: { c: 2, d: { e: [ 3, { f: { g: 4 } } ] } } }
const output =
traverse(x => `${x} a`, input)
console.log(output)
Here is a solution using object-scan. It works by building the solution at the same time as the input is traversed.
// const objectScan = require('object-scan');
const testObj = { a: 1, b: { c: 2, d: { e: 3, f: { g: 4 } } } };
const cloneAndModify = (obj) => objectScan(['**'], {
breakFn: ({ property, value, isLeaf, context }) => {
if (property === undefined) {
return;
}
const ref = context[context.length - 1];
if (!(property in ref)) {
ref[property] = isLeaf ? `${value} a` : {};
}
context.push(ref[property]);
},
filterFn: ({ context }) => {
context.pop();
}
})(obj, [{}])[0];
const r = cloneAndModify(testObj);
console.log(r);
// => { b: { d: { f: { g: '4 a' }, e: '3 a' }, c: '2 a' }, a: '1 a' }
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.7.1"></script>
Disclaimer: I'm the author of object-scan

Inclusive mixin with JS [duplicate]

This question already has answers here:
How to deep merge instead of shallow merge?
(47 answers)
Closed 3 years ago.
So I want to prevent overriding shared keys, when doing a mixin with JS, so we have:
const v = {
a: {
b: {
c: 4,
d: 'str'
}
}
};
console.log(Object.assign({}, v, {a: {b: {c: 5}}}));
this will log:
{ a: { b: { c: 5 } } }
but I am looking for this instead:
{ a: { b: { c: 5, d: 'str' } } }
anyone know how to do this (preferably without a library).
A fairly naïve solution which cannot handle cycles:
const mixin = (a, b) => {
for (let [key, val] of Object.entries(b)) {
if (typeof val !== 'object') {
a[key] = b[key];
continue;
}
if (val === null) {
a[key] = b[key];
continue;
}
if (!a.hasOwnProperty(key)) {
a[key] = b[key];
continue;
}
mixin(a[key], b[key]);
}
return a;
};
const mixinInclusive = (...v) => {
return v.reduce(mixin, {});
};
console.log(mixinInclusive(v, {a: {b: {c: 5}}}));

Categories

Resources