How do I shift child nodes of JSON to parent in JavaScript - javascript

I have a JSON object which is
let data = {
"key1" : 1,
"key2" : 2,
"subKeys":{
"subKey1" : 3,
"subKey2" : 4
}
}
I want my resultant JSON like this structure:
let resultantData = {
"key1":1,
"key2":2,
"subKey1":3,
"subKey2":4
}
How can I achieve this goal

Assuming you're using a fairly recent version of a JavaScript runtime due to the 'let' statement.
There are a couple of ways of doing this but the most direct is to merge the two hashes and then delete the key 'subKeys'.
I found this in another SO article. Please upvote if it helps.
let data = {
"key1" : 1,
"key2" : 2,
"subKeys":{
"subKey1" : 3,
"subKey2" : 4
}
};
let merged = { ...data, ...data['subKeys']};
delete merged['subKeys']
console.log(merged);

This should do it for you:
Object.entries(data).forEach(([key, val]) => {
if (typeof val === 'object' && !Array.isArray(val)) {
// if the item is an object and not an array, copy over the values.
Object.entries(val).forEach(([subKey, subVal]) => {
data[subKey] = data[subVal];
});
// delete original child object.
delete data[key];
}
})

To make this method more dynamic, you can use a recursive approach to flatten deeply nested objects with multiple levels. You can do this by taking the object's entries, and mapping them until no [key, value] pair array has a value which is an object by recursively mapping the object value's entires. To show this, I've added an additional nested object within your subKeys object called subsubKeys:
const data = { "key1" : 1, "key2" : 2, "subKeys":{ "subKey1" : 3, "subKey2" : 4, "subsubKeys": {"subsubKey1": 100}, "subKey3": 5 } };
const getEntries = obj => Object.entries(obj).flatMap(
([key, val]) => Object(val) === val
? getEntries(val)
: [[key, val]]
);
const flattenObj = obj => Object.fromEntries(getEntries(obj));
console.log(flattenObj(data));

Related

How to change the object key from nested array object and return array object

I have an array arr1 and an object arr2 with a nested config array.
If the object of arr1 has a key that matches with an id in that nested config and deeper questions array, then change that key (in the arr1 object) with the title property that is found next to that id.
Here is an example. The key isWorking from arr1 is the same as arr2.config[0].questions[0].id value, so
change that isWorking key to the value found in arr2.config[0].questions[0].custom.title.
var arr1= [
{"jobs": "Marketing","isWorking": yes,"country": "MY"},
{"country": "IN","members": 4}
]
var arr2=
{
"id":1,
"name":"xxx",
"config":[
{
"questions":[
{
"id":"isWorking",
"custom":{
"title":"Are you working?"
}
},
{
"id":"jobs",
"custom":{
"title":"Please specify job(s)"
}
}
]
},
{
"questions":[
{
"id":"country",
"custom":{
"title":"which Country?"
}
},
{
"id":"members",
"type":"choices",
"custom":{
"title":"How many members?"
}
}
]
}
]
}
Expected output:
[
{"Please specify job(s)": "Marketing","Are you working": yes,"which Country": "MY"},
{"which Country": "IN","How many members": 4}
]
I tried:
var result = arr1.map(e => ({
arr2.config.find(i => {
i.questions.find( q => {
q.id === Object.key(e) ? Object.key(e) === q.custom.title : q.id
}
})
}))
In your code the find callbacks do not return anything. When you have a code block (with braces) you need a return statement. Also, the outer object literal which you have as the return value of the map callback cannot have just the find call in it. It should have the proper object literal syntax, like with spread syntax. Moreover, the find method can only return an existing object, not a new, modified one.
I will assume here that the matching strings for the first object have to be found in the first questions array, and for the second object in the second questions array.
I also propose to rename arr2, because it isn't an array. It is a plain object, with a property that is an array (config).
Here is how you could do it with Object.fromEntries and Object.entries:
const arr1 = [{"jobs": "Marketing","isWorking": "yes","country": "MY"}, {"country": "IN","members": 4}];
const obj = {"id":1,"name":"xxx","config":[{"questions":[{"id":"isWorking","custom":{"title":"Are you working?"}},{"id":"jobs","custom":{"title":"Please specify job(s)"}}]},{"questions":[{"id":"country","custom":{"title":"which Country?"}},{"id":"members","type":"choices","custom":{"title":"How many members?"}}]}]}
const result = arr1.map((e, i) => Object.fromEntries(
Object.entries(e).map(([key, value]) =>
[obj.config[i].questions.find(({id}) =>
key === id
)?.custom?.title ?? key, value]
)
))
console.log(result);

Get all Indexes of Objects in Array - from Array in Object

i´m struggling with a Array in a Object stored in a Array with Objects from which I want return all Indicies.
Function to generate Object looks like this:
const addArray = function(a, b) {
const object = {
name: a,
rooms: b
};
testArray.push(object);
};
What I want to achieve is to cycle through the "testArray" and return every Index from the Object where the Array Rooms contains "Office" for example.
I´ve already tried to use a function like this but I don´t seem to be able to get the right Syntax for the Array in the Object:
function getAllIndexes(arr, val) {
var indexes = [], i = -1;
while ((i = arr.rooms.indexOf(val, i+1)) != -1){
indexes.push(i);
}
return indexes;
};
Thanks in advance!
Edit:
Additional Informations to Data:
A Object with data filled would look like this:
const device = {
name: "TV",
rooms: ["Living Room", "Bedroom"]
};
After generating Objects like this I push them to an array witch only contains this objects (see function addArray)
You can use Array.flatMap() to map each value of the array at matches val to it's index, and the rest to empty array, which will be removed by the flatMap:
const getAllIndexes =(arr, val) => arr.flatMap((v, i) => v === val ? i : [])
const arr = [1, 2, 3, 1, 2, 1, 1, 2]
const result = getAllIndexes(arr, 1)
console.log(result)
Using your array of objects, you'll need to compare a value, or check if an object meets some condition. It's better in this case to replace val with a predicate function:
const getAllIndexes =(arr, pred) => arr.flatMap((v, i) => pred(v) ? i : [])
const arr = [{ rooms: [1, 2, 3] }, { rooms: [2, 1, 1] }, { rooms: [3, 2, 2] }, { rooms: [1, 2, 1] }]
const result = getAllIndexes(arr, o => o.rooms.includes(1))
console.log(result)
Try using Array.prototype.map and Array.prototype.filter
function getAllIndexes(arr, val) {
return arr.map(i=> {
let room = i.rooms;
return room.indexOf(val);
}).filter(a=>{
a != -1;
});
};
You could destructure rooms from the device and get the index, if the wanted value is found.
const
room = 'Office',
indices = array.flatMap(({ rooms }, i) => rooms.includes(room) ? i : []);
The above code features a former solution from me with hacking Array#flatMap.

How to alter some values of an array in react

I am working on react project. I have an array which is not a state variable but a constant. The array contains undefined values as its element. I want to make undefined values as empty array. The array finally should contain values without undefined values. Can anyone help to solve this issue?
if array is [undefined, 1,2,[] ], i need to convert it to [[], 1,2,[] ]
array.forEach(dataSet =>
(dataSet.dataPoint = (dataSet.dataPoint === undefined) ? [] : dataSet.dataPoint)
);
you can map your array into another array:
const array2 = array1.map(item => {
return typeof item === 'undefined'
? []
: item
})
https://codepen.io/giannidk/pen/PoYQvBM?editors=0011
1) Use map as you are trying to return array as forEach just iterates over the array.
const array = [undefined, 1,2,[] ]
let newArr = array.map((dataSet) => {
return dataSet = (dataSet === undefined) ? [] : dataSet
})
console.log(newArr) // [[], 1, 2,[]]
You could map over the array and return the value of what you'd like in the new array:
array = array.map(dataSet =>
(dataSet.dataPoint = (dataSet.dataPoint === undefined) ? [] : dataSet.dataPoint)
);
let array = [{}, { dataPoint: 1 }, {}, { dataPoint: 2 }];
array = array.map(dataSet =>
(dataSet.dataPoint = (dataSet.dataPoint === undefined) ? [] : dataSet.dataPoint)
);
console.log(array)
An approach to mutate the original array (rather than reassign / create a new array):
const array = [{}, {
dataPoint: 1
}, {}, {
dataPoint: 2
}];
array.forEach((dataSet, index) => {
array[index] = dataSet.dataPoint === undefined ? [] : dataSet.dataPoint
});
console.log(array)

Get all Keys/Array keys in JSON parse Object?

I have sample JSON like this
`{
values:[{
"name": "Base Url",
"url": "https://kubemanagement-prod.kohls.com"
},
{
"name": "Base Url newwww",
"url": "https://batman.com"
}]
}`
Currently when I add this paticular JSON to the lodash _.keys gives me the result ["0", "1"] which is basically the index of first and second object 0 and 1.
What I exactly want is to retrieve all the keys of the JSON object including sub object properties as well. In this case ["values","0", "1","name","url"]
Does anyone knows a lodash method or a mechanism to retrieve all the keys given in complex JSON object to nth level?
language : Angular + Typescript
This function recursively gets the keys from an objects tree, using _.keys() and _.flatMap() and _.union() to combine lists of keys, and get only the unique values:
const getAllKeys = obj => _.union(
_.keys(obj),
_.flatMap(obj, o => _.isObject(o) ? getAllKeys(o) : [])
)
const arr = {"values": [{"name":"Base Url","url":"https://kubemanagement-prod.kohls.com"},{"name":"Base Url newwww","url":"https://batman.com"}]}
const result = getAllKeys(arr)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
And the same idea without lodash, using Object.values() and Array.flatMap() to iterate the current object (or array), and Array.concat() and a Set to make the keys unique:
const getAllKeys = obj => [...new Set([].concat(
Object.keys(obj),
Object.values(obj).flatMap(o => typeof o === 'object' ? getAllKeys(o) : [])
))]
const arr = {"values": [{"name":"Base Url","url":"https://kubemanagement-prod.kohls.com"},{"name":"Base Url newwww","url":"https://batman.com"}]}
const result = getAllKeys(arr)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
Without array indexes:
const getAllKeys = obj => _.union(
_.isArray(obj) ? [] : _.keys(obj),
_.flatMap(obj, o => _.isObject(o) ? getAllKeys(o) : [])
)
const arr = {"values": [{"name":"Base Url","url":"https://kubemanagement-prod.kohls.com"},{"name":"Base Url newwww","url":"https://batman.com"}]}
const result = getAllKeys(arr)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
You don't need lodash for this, Object.keys is sufficient. You just write a recursive function, writing to a Set, perhaps converting to array when you're done:
const array = [
{
"name": "Base Url",
"url": "https://kubemanagement-prod.kohls.com"
},
{
"name": "Base Url newwww",
"url": "https://batman.com"
}
];
function addAll(set, entries) {
for (const entry of entries) {
set.add(entry);
}
return set;
}
function allKeys(obj/*: object|array*/, keys/*: Set<string>*/ = new Set()) {
addAll(keys, Object.keys(obj));
for (const entry of Object.values(obj)) {
if (entry && typeof entry === "object") {
allKeys(entry, keys);
}
}
return keys;
}
console.log([...allKeys(array)]);
Or using the structure in your edit:
const array = {
values:[{
"name": "Base Url",
"url": "https://kubemanagement-prod.kohls.com"
},
{
"name": "Base Url newwww",
"url": "https://batman.com"
}]
}
function addAll(set, entries) {
for (const entry of entries) {
set.add(entry);
}
return set;
}
function allKeys(obj/*: object|array*/, keys/*: Set<string>*/ = new Set()) {
addAll(keys, Object.keys(obj));
for (const entry of Object.values(obj)) {
if (entry && typeof entry === "object") {
allKeys(entry, keys);
}
}
return keys;
}
console.log([...allKeys(array)]);
How about
const arr = {"values": [{"name":"Base Url","url":"https://kubemanagement-prod.kohls.com"},{"name":"Base Url newwww","url":"https://batman.com"}]}
let result1 = Object.keys(arr) // get the object keys which is "values"
let result2 = Object.keys(arr[result1]); // get the keys of the object name "0,1" here
let result3 = Object.keys(arr[result1][0]); // get property names of one object "name and url" here
let resutltant = result1.concat(result2, result3) // "merge array"
console.log(resutltant)
Assuming that your object properties names will remain constant
If you use typescript why don't use Object.entries or Object.values to do that?
You need to add to tsconfig the next line:
lib: [
......
"es2018",
]

Search for values of a specific key in a nested object

I have this nested object:
{
"PINS" : {
"2017" : {
"Nov-2017" : {
"VJkRWX7pTSl_5w1Np" : {
"pin" : "6K3jP5vLyN",
"say": "Hello"
},
"MsdsXiO9G9mwM3Qa" : {
"pin" : "hnPKh7ywvT",
"say": "Hello"
}
},
"Dec-2017" : {
"Mm35Gjb-nY0k2TV" : {
"pin" : "xWwaNNE2XG",
"say": "Hello"
},
"WzajCLEJmJHmzg0" : {
"pin" : "vMU1mKbZAi",
"say": "Hello"
}
}
},
"2018" : {
"Jan-2018" : {
"Wf8E1unVaOh03a43" : {
"pin" : "qXJCQREATD",
"say": "Hello"
},
"JZqP8fVCLSja6J82v" : {
"pin" : "o5D8S8Lvtb",
"say": "Hello"
}
},
"Feb-2018" : {
"lMMAKNLy8jtnnXAN" : {
"pin" : "9zDuHcw6qH",
"say": "Hello"
},
"e9EV3HDKCceM" : {
"pin" : "kPllwcoaob",
"say": "Hello"
}
}
}
}
}
what I need is to find for all the 'pin' keys, and get their values, to put them into an array.
Exactly, I need to have an array like this:
['6K3jP5vLyN', 'hnPKh7ywvT', 'xWwaNNE2XG', 'vMU1mKbZAi', 'qXJCQREATD', 'o5D8S8Lvtb', '9zDuHcw6qH', 'kPllwcoaob']
I have tried:
const array = [];
function iter(obj){
for(key in obj){
if(obj.pin)
array.push(obj.pin);
if(obj[key]!==null && typeof obj[key]==="object"){
iter(obj[key]);
}
}
}
iter(obj);
But I get the values of each key twice. Is there some improved way to do this?
You can use a recursive method to flatten the nested object to it's leaf values.
Option 1 - the value is the only property on the leaf (original answer before question updated)
The method extracts the current values to an array using Object.values(). It iterates the array with Array.map(), and flattens any value which is an object. The result of each run is spread into Array.concat() to flatten the nested arrays.
const data = {"PINS":{"2017":{"Nov-2017":{"VJkRWX7pTSl_5w1Np":{"pin":"6K3jP5vLyN"},"MsdsXiO9G9mwM3Qa":{"pin":"hnPKh7ywvT"}},"Dec-2017":{"Mm35Gjb-nY0k2TV":{"pin":"xWwaNNE2XG"},"WzajCLEJmJHmzg0":{"pin":"vMU1mKbZAi"}}},"2018":{"Jan-2018":{"Wf8E1unVaOh03a43":{"pin":"qXJCQREATD"},"JZqP8fVCLSja6J82v":{"pin":"o5D8S8Lvtb"}},"Feb-2018":{"lMMAKNLy8jtnnXAN":{"pin":"9zDuHcw6qH"},"e9EV3HDKCceM":{"pin":"kPllwcoaob"}}}}};
const flattenObj = (obj) =>
[].concat(...Object.values(obj).map((o) => typeof o === 'object' ? flattenObj(o) : o));
const result = flattenObj(data);
console.log(result);
Option 2 - the value is not the only property on the leaf
If your data contains other keys, this variant uses Object.entries() to extract a specific key:
const data = {"PINS":{"2017":{"Nov-2017":{"VJkRWX7pTSl_5w1Np":{"pin":"6K3jP5vLyN","say":"Hello"},"MsdsXiO9G9mwM3Qa":{"pin":"hnPKh7ywvT","say":"Hello"}},"Dec-2017":{"Mm35Gjb-nY0k2TV":{"pin":"xWwaNNE2XG","say":"Hello"},"WzajCLEJmJHmzg0":{"pin":"vMU1mKbZAi","say":"Hello"}}},"2018":{"Jan-2018":{"Wf8E1unVaOh03a43":{"pin":"qXJCQREATD","say":"Hello"},"JZqP8fVCLSja6J82v":{"pin":"o5D8S8Lvtb","say":"Hello"}},"Feb-2018":{"lMMAKNLy8jtnnXAN":{"pin":"9zDuHcw6qH","say":"Hello"},"e9EV3HDKCceM":{"pin":"kPllwcoaob","say":"Hello"}}}}};
const flattenObjKey = (obj, key) =>
[].concat(...Object.entries(obj)
.map(([k, v]) => typeof v === 'object' ?
flattenObjKey(v, key) : (k === key ? v : [])
));
const result = flattenObjKey(data, 'pin');
console.log(result);
It looks like your data is consistently structured, so this is fairly simple.
const pins = [];
for (let year in pins) {
for (let month in year) {
for (let key in months) {
pins.push(key.pin);
}
}
}
If it's not consistently structured, you'll need to extract a recursive solution that looks for pin keys.

Categories