'How to create a nested data structure from 2 arrays?
Hi guys I get confused trying to qcheave this, so any help would be great!
I have two arrays from which I need a specific object. Source arrays:
1-Types (1D): ['dev', 'env', 'othr']
2-Services (2D): (3) [Array(3), Array(4), Array(6)]
Array(3)
0: "'1':'Option 11'"
1: "'2':'Option 12'"
2: "'3':'Option 13'"
Array(4)
0: "'5':'Option 21'"
1: "'6':'Option 22'"
2: "'7':'Option 23'"
3: "'4':'Option 24'"
Array(6)
0: "'8':'Option 31'"
1: "'9':'Option 32'"
2: "'10':'Option 33'"
3: "'11':'Option 34'"
4: "'12':'Option 35'"
5: "'13':'Option 36'"
The desired outcome is this structure:
servData = {
"dev": {
"1": "Option 11",
"2": "Option 12",
"3": "Option 13"
},
"env": {
"4": "Option 21",
"5": "Option 22",
"6": "Option 23",
"7": "Option 24"
},
"othr": {
"8": "Option 31",
"9": "Option 32",
"10": "Option 33",
"11": "Option 34",
"12": "Option 35",
"13": "Option 36"
}
};
Solution 1:[1]
Your data did need some cleanup. NOTE: Data has been updated and .map(a => a.replace(/'/g,''))
has been added to remove all '
.
Use Array#map
and Array#reduce
as follows:
const keys = ['dev', 'env', 'othr'],
input = [ [ "'1':'Option 11'", "'2':'Option 12'", "'3':'Option 13'" ], [ "'5':'Option 21'", "'6':'Option 22'", "'7':'Option 23'", "'4':'Option 24'" ], [ "'8':'Option 31'", "'9':'Option 32'", "'10':'Option 33'", "'11':'Option 34'", "'12':'Option 35'", "13:'Option 36'" ] ],
output = keys.reduce(
(o, key,i) =>
({
...o,
[key]: input[i].map(a => a.replace(/'/g,'')).reduce(
(acc,cur) =>
({...acc,[cur.split(':')[0]]:cur.split(':')[1]}),{})
}),{}
);
console.log( output );
In order to clean out the '
programmatically, especially the one after :
use the following code:
const keys = ['dev', 'env', 'othr'],
input = [ [ "'1':'Option 11'", "'2':'Option 12'", "'3':'Option 13'" ], [ "'5':'Option 21'", "'6':'Option 22'", "'7':'Option 23'", "'4':'Option 24'" ], [ "'8':'Option 31'", "'9':'Option 32'", "'10':'Option 33'", "'11':'Option 34'", "'12':'Option 35'", "'13':'Option 36'" ] ],
output = input.map(s => s.map(e => e.replace(/'/g,'').split(':')))
.reduce(
(o, arr,i) =>
({...o, [keys[i]]: arr.reduce((acc,[k,v]) => ({...acc,[k]:v}), {})}),{}
);
console.log( output );
Please note that, as pointed out by @PeterSeliger, replacing '
globally may have unintended consequences. For instance if you had a value such as "'14':'Option's Best'"
would become "14:Options Best"
. So consider a more targetted approach. Maybe use of Function(...)()
would avoid the need for replacing all the '
's. See the demo below:
const keys = ['dev', 'env', 'othr'],
input = [ [ "'1':'Option 11'", "'2':'Option 12'", "'3':'Option 13'" ], [ "'5':'Option 21'", "'6':'Option 22'", "'7':'Option 23'", "'4':'Option 24'" ], [ "'8':'Option 31'", "'9':'Option 32'", "'10':'Option 33'", "'11':'Option 34'", "'12':'Option 35'", "'13':'Option 36'" ] ],
output = input
.reduce(
(o, arr,i) =>
({
...o,
[keys[i]]: arr.reduce(
(acc,cur) =>
({...acc,...Function(`return {${cur}}`)()}), {})
}),{}
);
console.log( output );
Solution 2:[2]
Use this:
const itemNames = ["dev", "env", "othr"];
const items = [
["'1':'Option 11'", "'2':'Option 12'", "'3':'Option 13'"],
["'5':'Option 21'", "'6':'Option 22'", "'7':'Option 23'", "'4':'Option 24'"],
[
"'8':'Option 31'",
"'9':'Option 32'",
"'10':'Option 33'",
"'11':'Option 34'",
"'12':'Option 35'",
"'13':'Option 36'"
]
];
const value = itemNames
.map((value, index) => ({ index, value }))
.reduce((p, el) => {
p[el.value] = {};
items[el.index].forEach((e) => {
let [op, ...va] = e.split(":");
op = op.substring(1, op.length - 1);
va = va.join(":");
va = va.substring(1, va.length - 1);
const ar = va;
p[el.value][parseInt(op)] = ar;
});
return p;
}, {});
console.log(value);
.as-console-wrapper { min-height: 100%!important; top: 0; }
Solution 3:[3]
The OP's provided array of stringified entries seems to be mal-formatted ... e.g. "1':'Option 11'"
, with either a missing or a dispensable single quote for the key
part, instead of "'1':'Option 11'"
(with single quotes) or "1:'Option 11'"
(without single quotes for the key part).
Because of that the beneath presented approach uses a capturing regex like /^'?(?<key>.*?)'?\s*:\s*'?(?<value>.*?)'?$/
in order to sanitize (or rather be agnostic to) each provided string based key
-value
configuration.
The approach itself makes use of Array.prototype.reduce
twice.
The outer reduce
does process the list of property or item names as entryKey
together with a passed entries-config
which features each property name's related array of stringified key-value pairs.
The inner reduce
does process such an array, retrieves key
and value
from the string and, based on this data, assigns a new entry to the value which, within the outer reduce
, gets assigned to the newly created entryKey
related entry.
function createEntryFromKeyAndEntriesConfig(
{ config = [], result = {} },
entryKey,
idx,
) {
// see ... [https://regex101.com/r/DQbrRM/3]
const regXKeyValue =
/^'?(?<key>.*?)'?\s*:\s*'?(?<value>.*?)'?$/;
result[entryKey] = config[idx]
.reduce((obj, entryString) => {
const { key = '', value = '' } = regXKeyValue
.exec(entryString)
?.groups;
obj[key] = value;
return obj;
}, {});
return { config, result };
}
const entriesConfig = [[
"1':'Option 11'",
"2':'Option 12'",
"3':'Option 13'",
], [
"5':'Option's 21st'",
"6':'Option\'s 22nd'",
"7':'Option's 23rd'",
"4':'Option\'s 24th'",
], [
"8':'Option 31'",
"9':'Option 32'",
"10':'Option 33'",
"11':'Option 34'",
"12':'Option 35'",
"13':'Option 36'",
]];
const propertyNames = ['dev', 'env', 'othr'];
const { result: servData } = propertyNames
.reduce(createEntryFromKeyAndEntriesConfig, {
config: entriesConfig,
result: {},
});
console.log({ servData });
.as-console-wrapper { min-height: 100%!important; top: 0; }
Solution 4:[4]
Try this :
const arr1 = ['dev', 'env', 'othr'];
const arr2 = [["'1':'Option 11'", "'2':'Option 12'", "'3':'Option 13'"],
["'5':'Option 21'", "'6':'Option 22'", "'7':'Option 23'", "'4':'Option 24'"],
["'8':'Option 31'", "'9':'Option 32'", "'10':'Option 33'", "'11':'Option 34'", "'12':'Option 35'", "'13':'Option 36'"]];
const obj = {};
arr1.forEach((item, index) => {
const innerObj = {};
arr2[index].forEach(innerItem => {
innerObj[innerItem.split(':')[0].replace(/['"]+/g, '')] = innerItem.split(':')[1].replace(/['"]+/g, '')
});
obj[item] = innerObj;
});
console.log(obj)
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | |
Solution 3 | |
Solution 4 | Rohìt JÃndal |