creating an multidimensional Object dynamically from an multidimensional array - javascript

Im trying to create an multidimensional Object like this one:
{ A : {a1: {},a2:{}}, B: {b1:{},b2:{}}}
from an multidimensional array like this one:
let array1 = [
['A', 'a1'],
['A', 'a1'],
['A', 'a2'],
['B', 'b1'],
['B', 'b1'],
['B', 'b2'],
];
I'm trying this for some hours now and was also reading plenty of entrys here on stackoverflow, but nothing really fits this specific case.
What i did so far:
let array1 = [
['A', 'a1'],
['A', 'a1'],
['A', 'a2'],
['B', 'b1'],
['B', 'b1'],
['B', 'b2'],
];
let object1 = {};
array1.forEach(function (subArray) {
let level1 = subArray[0];
let level2 = subArray[1];
object1[[level1]] = { ...{ [level2]: {} } };
});
console.log('object: ', object1);
//desired output: object = { A : {a1: {},a2:{}}, B: {b1:{},b2:{}}}
//what I get: object = { A : {a2:{}}, B: {b2:{}}}
So somehow in my code the entrys like {a1: {}} are getting overwritten in each iteration instead of adding a new entry.
Thanks a lot in advance.

You can use Array.reduce() along with some destructuring to get the required object output.
For each key, value pair in the original array, we add a property to the final object, (e.g. 'A'), then add a new object for each value in the array (e.g. 'a1', 'a2').
let array1 = [ ['A', 'a1'], ['A', 'a1'], ['A', 'a2'], ['B', 'b1'], ['B', 'b1'], ['B', 'b2'], ];
let output = array1.reduce((acc, [k,v]) => {
acc[k] = { ...(acc[k] || {}), [v]: {} };
return acc;
}, {})
console.log(output)
.as-console-wrapper { max-height: 100% !important; top: 0; }
One could also do this in a one-liner, but I think it's a lot harder to read (and understand):
let array1 = [ ['A', 'a1'], ['A', 'a1'], ['A', 'a2'], ['B', 'b1'], ['B', 'b1'], ['B', 'b2'], ];
let output = array1.reduce((acc, [k,v]) => ({ ...acc, [k]: { ...(acc[k] || {}), [v]: {} } }), {});
console.log(output)
.as-console-wrapper { max-height: 100% !important; top: 0; }

Keep previous properties using ...object1[[level1]]:
let array1 = [
['A', 'a1'],
['A', 'a1'],
['A', 'a2'],
['B', 'b1'],
['B', 'b1'],
['B', 'b2'],
];
let object1 = {};
array1.forEach(function (subArray) {
let level1 = subArray[0];
let level2 = subArray[1];
object1[[level1]] = {
...object1[[level1]], // Keep previous properties
...{ [level2]: {} } // Add new
};
});
console.log('object: ', object1);

Related

Use array as keys for nested array?

I have a nested array like shown below.
var arr1 = ['a', 'b', ['c', 'd'], ['e', 'f']];
Is there a way to use another, un-nested array as the key(s) for arr1?
var arr1 = ['a', 'b', ['c', 'd'], ['e', 'f']];
var arr2 = [3, 0];
var arr3 = [4, 1];
console.log(arr1[arr2]); // should return 'c' like arr1[3][0]
console.log(arr1[arr3]); // instead of arr1[4][1]; should return 'f'
Is there a function that allows me to do this?
You can make your own function that implements this behavior like so:
function nested_access(arr1, arr2) {
let ret = arr1;
arr2.forEach((index) => {
ret = ret[index];
});
return ret;
}
const arr1 = ['a', 'b', ['c', 'd'], ['e', 'f']];
const arr2 = [2, 0];
const arr3 = [3, 1];
console.log(nested_access(arr1, arr2)); // should return 'c'
console.log(nested_access(arr1, arr3)); // should return 'f'
Just a simple method with reduce to walk the tree. (I changed your example indexes since they were off)
const lookUp = (arr, indexes) =>
indexes.reduce(
(acc, index) => acc[index],
arr);
var arr1 = ['a', 'b', ['c', 'd'],
['e', 'f']
];
var arr2 = [2, 0];
var arr3 = [3, 1];
console.log(lookUp(arr1, arr2));
console.log(lookUp(arr1, arr3));
You can find the value by iteratively accessing properties using an array of property accessors:
function findValue (obj, propertyAccessors) {
try {
let result = obj;
for (const key of propertyAccessors) result = result[key];
return result;
}
catch {
return undefined;
}
}
const arr1 = ['a', 'b', ['c', 'd'], ['e', 'f']];
const arr2 = [2, 0];
const arr3 = [3, 1];
console.log(findValue(arr1, arr2)); // "c"
console.log(findValue(arr1, arr3)); // "f"
console.log(findValue(arr1, [2, 2, 3])); // undefined
If you attempt to access a property which doesn't exist, the result will be undefined. If you continue to attempt to access another property on undefined, an exception will be thrown. By using try...catch, you can catch such an error and return undefined.
Try this:
function getValue(arr, arrKeys)
{
return arrKeys.reduce(
(acum, current) => acum?.[current]
, arr
)
}
var arr1 = ['a', 'b', ['c', 'd'], ['e', 'f']];
var arr2 = [2, 0];
var arr3 = [3, 1];
console.log(getValue(arr1, arr2))
console.log(getValue(arr1, arr3))
You can create an array method Array#getNested as follows:
Array.prototype.getNested = function(index) {
let out = this;
index.forEach(i => out = out[i]);
return out;
}
const arr1 = ['a', 'b', ['c', 'd'], ['e', 'f'], ['g',['h',['i', 'j']]]];
const arr2 = [2, 0];
const arr3 = [3, 1];
const arr4 = [4, 1, 1, 0];
console.log( arr1.getNested(arr2) );
console.log( arr1.getNested(arr3) );
console.log( arr1.getNested(arr4) );

unpack array element inot object as key in typescript

anyway I can unpack an array into an object that each element of array as the key of object and value is 0? something like
const arr = ['a', 'b', 'c', 'd']
//I want to have
//const obj = {'a': 0, 'b': 0, 'c': 0}
Use Object.fromEntries combined with the array .map method:
const arr = ['a', 'b', 'c', 'd']
//I want to have:
//const obj = {'a': 0, 'b': 0, 'c': 0}
console.log(Object.fromEntries(arr.map(x => [x, 0])))
You could use Array.prototype.reduce() method to make your required object.
const arr = ['a', 'b', 'c', 'd'];
const ret = arr.reduce((prev, c) => {
const p = prev;
p[c] = 0;
return p;
}, {});
console.log(ret);

How to map an array of arrays into an array of objects with a given keys array?

From an array of keys and an array of arrays, like this:
const keys = ['foo', 'bar'];
const vals = [
['a', 'A'],
['b', 'B']
];
How to get an array of objects like below ?
[
{'foo' : 'a', 'bar' : 'A'},
{'foo' : 'b', 'bar' : 'B'}
]
Maybe using lodash ?
You can use loash's _.zipObject() to create an object from an array of keys and values for each value array inside your 2d array using the _.map() method:
const keys = ['foo', 'bar']
const vals = [
['a', 'A'],
['b', 'B']
];
const res = _.map(vals, arr => _.zipObject(keys, arr));
console.log(res);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>
If you prefer vanilla JS, then you could use Object.fromEntries() on a zipped array (created using .map()):
const keys = ['foo', 'bar']
const vals = [
['a', 'A'],
['b', 'B']
];
const res = vals.map(
arr => Object.fromEntries(arr.map((v, i) => [keys[i], v]))
);
console.log(res);
To be more generic, you can use Array.reduce() with index variable
const keys = ['foo', 'bar']
const values = [
['a', 'A'],
['b', 'B']
]
const mapped = values.map(val => val.reduce((acc, cur, i) => ({...acc, [keys[i]]: cur}),{}))
console.log(mapped)
With lodash/fp you can generate a function using _.flow(), that curries _.zipObject() with the keys, and the _.map() with the curried _.zipObject(), and then you can call it with vals to get the array of objects:
const fn = _.flow(_.zipObject, _.map);
const keys = ['foo', 'bar']
const vals = [
['a', 'A'],
['b', 'B']
];
const result = fn(keys)(vals);
console.log(result);
<script src='https://cdn.jsdelivr.net/g/lodash#4(lodash.min.js+lodash.fp.min.js)'></script>
You can do it simply using reduce.
let keys = ['foo', 'bar'];
let values = [
['a', 'A'],
['b', 'B']
];
const res = values.reduce((a, [first, second]) => {
return [...a, {[keys[0]]: first, [keys[1]]: second}];
}, []);
console.log(res);
.as-console-wrapper{min-height: 100%!important; top:0}
let dataKeys = ['foo', 'bar'];
let dataValues = [
['a', 'A'],
['b', 'B']
];
let transformed = dataValues.reduce((result,item)=>{
result.push(
dataKeys.reduce((r,dk,index)=>{
let o = {};
o[dk]= item[index];
return {...r, ...o}
},{})
)
return result
},[]);
console.log(JSON.stringify(transformed,null,2));

Returning the array in an array of arrays with a value from another array

I have and array of arrays aa = [['a'], ['b'], ['c']] and i have an array a = ['a', 'b', 'c']
I need to get the item in aa for each element in a i.e i want to list elements in a with their respective arrays in aa the result should be like
a: ['a'] b: ['b'] c: ['c']
I tried this code but it does return the first element i aa for each element in a
I wonder what's wrong here
const aa = [
['a'],
['b'],
['c']
]
const a = ['a', 'b', 'c']
let b = []
a.forEach((el) => {
b.push(
aa.filter((element) => {
return element.includes(el)
})
)
})
console.log(b)
Try this
const aa = [
['a'],
['b'],
['c']
];
const a = ['a', 'b', 'c'];
let b = {};
a.forEach( // loop "a"
aEl => b[aEl] = aa.filter( // filter "aa"
aaEl => aaEl.includes(aEl) // on array that includes the item from 'a'
).flat() // we need to flatten the resulting array before returning it
);
console.log(JSON.stringify(b)); // using stringify to make it readable
Since you want your output to be a key-value list (a: ['a']), variable b should be a map. Let's also rename b to out for readability.
out = {}
To get a better view of if our code is working, let's use some unique test data, and let's rename a to keys and aa to values.
const keys = ['A', 'B', 'C']
const values = [
['A', 'A2', 'a3'],
['B1', 'B', 'b3'],
['C1', 'C2', 'C']
]
For every key in keys, we want to set search for all arrays in values that contain the key. To set the search result to out we use brackets like so:
keys.forEach((key) => {
out[key] = values.filter(valueArr => valueArr.includes(key))
})
This outputs:
{
"A": [["A", "A2", "a3"]],
"B": [["B1", "B", "b3"]],
"C": [["C1", "C2", "C"]]
}
Now there are two arrays around each value. This is because values.filter can return multiple arrays. To combine these into a single array you can use the flat() function. The whole code looks like:
const keys = ['A', 'B', 'C']
const values = [
['A', 'A2', 'a3'],
['B1', 'B', 'b3'],
['C1', 'C2', 'C']
]
out = {}
keys.forEach((key) => {
out[key] = values.filter(valueArr => valueArr.includes(key)).flat()
})
console.log(out)

insert value in middle of every value inside array

I have a array has a value of ['a', 'b', 'c', 'd', 'e'] now I want it to become a object that have it's value so I do some array mapping
const arrs = ['a', 'b', 'c', 'd', 'e']
let arrObj = arrs.map(arr => {
return {value: arr}
})
Now the value of arrObj is
[{value: 'a'}, {value: 'b'}, {value: 'c'}, {value: 'd'}, {value: 'e'}]
But what I want to do is to insert a object in the middle of each object that is inside the array that has a value of {operator: '+'} so the value of arrObj will be
[{value: 'a'}, {operator: '+'}, {value: 'b'}, {operator: '+'}, {value: 'c'}, {operator: '+'}, {value: 'd'}, {operator: '+'}, {value: 'e'}]
now, using javascript, how can I achive that function given that I'm setting a value of arrObj in array.map() ?
You could create a new array with a double length minus one and add the required values, depending on the index.
var values = ['a', 'b', 'c', 'd', 'e'],
result = Array.from(
{ length: values.length * 2 - 1 },
(_, i) => i % 2
? { operator: '+' }
: { value: values[i >> 1] }
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
One option is to map each element (but the last) to an array with that element and another with the +, then flatten:
const arrs = ['a', 'b', 'c', 'd', 'e'];
const transformed = arrs
.map((char, i) => (
i === arrs.length - 1
? [{ value: char }]
: [{ value: char }, { value: '+' }]
))
.flat();
console.log(transformed);
If + won't appear in the input array, then you can join by + initially, then split:
const arrs = ['a', 'b', 'c', 'd', 'e'];
const output = arrs
.join('+')
.split('')
.map(value => ({ value }));
console.log(output);
Use flatMap and return a pair from the callback. Finally, remove the extra element.
const arrs = ['a', 'b', 'c', 'd', 'e']
let arrObj = arrs.flatMap(x => [
{operator: '+'}, {value: x}
]).slice(1)
console.log(arrObj)
If your platform doesn't have flatMap yet, it's trivial to polyfill:
Array.prototype.flatMap = function(fn) {
return this.concat.apply([], this.map(fn))
}
Generic function:
let interleave = (ary, val) => ary.flatMap(x => [val, x]).slice(1);
//
let arrObj = interleave(
arrs.map(x => ( {value: x})),
{operator: '+'}
)
What about using reduce?
let arrObj = arrs.reduce((acc, curr, index) => {
if (index === arrs.length - 1) acc = [...acc, {value: curr}];
else acc = [...acc, {value: curr}, {operator: '+'}];
return acc;
}, [])
Concat your required object and deep flatten it like below:
var arrs = ['a', 'b', 'c', 'd','e'];
const arrObj = arrs.map((arr,i) => {
let item = [{value: arr}];
if(i < arrs.length-1)
item.push({operator: '+'});
return item;
});
console.log(flattenDeep(arrObj));
function flattenDeep(arr1) {
return arr1.reduce((acc, val) => Array.isArray(val) ? acc.concat(flattenDeep(val)) : acc.concat(val), []);
}
For more details take a look here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat

Categories

Resources