I need help converting several arrays:
x = ['a', 'b', 'c']
y = ['d', 'e', 'f']
z = ['d', 'g', 'h']
Into a single JSON:
{
a: { b: { c: 'done' }},
d: { e: { f: 'done' },
g: { h: 'done' }}
}
Is this possible using recursion? I can't seem to get it working properly so I was wondering if there is already an easy way to do this in JS.
var struct = {};
var paths = [
['a', 'b', 'c'],
['d', 'e', 'f'],
['d', 'g', 'h']
];
paths.forEach(function (path) {
var ref;
ref = struct;
path.forEach(function (elem, index) {
if (!ref[elem]) {
ref[elem] = index === path.length - 1 ? "done": {};
}
ref = ref[elem];
});
});
console.log(JSON.stringify(struct, null, "\t"));
Output:
{
"a": {
"b": {
"c": "done"
}
},
"d": {
"e": {
"f": "done"
},
"g": {
"h": "done"
}
}
}
Note: this script will fail if your input is like:
var paths = [
['a', 'b', 'c'],
['a', 'b', 'c', 'd' ]
];
It decided c should be "done" but then there is another level. maybe this wont ever happen, if it does, figure out what you want the result to be.
A version with Array#forEach() and Array#reduce()
var x = ['a', 'b', 'c'],
y = ['d', 'e', 'f'],
z = ['d', 'g', 'h'],
object = function (array) {
var o = {};
array.forEach(function (a) {
var last = a.pop();
a.reduce(function (r, b) {
r[b] = r[b] || {};
return r[b];
}, o)[last] = 'done';
});
return o;
}([x, y, z]);
document.write('<pre>' + JSON.stringify(object, 0, 4) + '</pre>');
Related
I need the solution for the general case
for example
let data = [['a', 'b'],['c', 'd'],['e', 'f', 'g', 'h']];
I need this:
{
"a": {
"c": {
"e": 0,
"f": 0,
"g": 0,
"h": 0
},
"d": {
"e": 0,
"f": 0,
"g": 0,
"h": 0
}
},
"b": {
"c": {
"e": 0,
"f": 0,
"g": 0,
"h": 0
},
"d": {
"e": 0,
"f": 0,
"g": 0,
"h": 0
}
}
}
and data can be any random array of arrays... I tried a recursive approach but I get stuck with Map and .fromEntries method...
Simple recursion:
Base case - we only have one array inside the array. We construct an object with default values from it.
Recursive step - we have more arrays. We build up an object and each key comes from the first array, each value is a recursive call that uses the rest of the arrays:
const buildObj = (data, defaultValue = 0) => {
if (data.length > 1)
return Object.fromEntries(
data[0].map(x => [x, buildObj(data.slice(1), defaultValue)])
)
return Object.fromEntries(
data[0].map(x => [x, defaultValue])
);
}
console.log(buildObj([
['e', 'f', 'g', 'h']
]));
console.log(buildObj([
['c', 'd'],
['e', 'f', 'g', 'h']
]));
console.log(buildObj([
['a', 'b'],
['c', 'd'],
['e', 'f', 'g', 'h']
]));
console.log(buildObj([
['a', 'b'],
['c', 'd'],
['e', 'f', 'g', 'h']
], 42)); //different default
Can also be represented by:
Base case - return the default value.
Recursive step - We build up an object and each key comes from the first array, each value is a recursive call that uses the rest of the arrays.
const buildObj = (data, defaultValue = 0) => {
if (data.length !== 0)
return Object.fromEntries(
data[0].map(x => [x, buildObj(data.slice(1), defaultValue)])
);
return defaultValue;
}
console.log(buildObj([
['e', 'f', 'g', 'h']
]));
console.log(buildObj([
['c', 'd'],
['e', 'f', 'g', 'h']
]));
console.log(buildObj([
['a', 'b'],
['c', 'd'],
['e', 'f', 'g', 'h']
]));
console.log(buildObj([
['a', 'b'],
['c', 'd'],
['e', 'f', 'g', 'h']
], 42)); //different default
I think this runs good. Runs recursion with step. You can change forEach to for loop, if you want.
let data = [['a', 'b'],['c', 'd'],['e', 'f', 'g', 'h']];
const arrToDict = (data) => {
const recursive = (depth = 0) => {
let dict = {}
if (data.length === depth + 1) {
data[depth].forEach(el => {
dict[el] = 0
})
} else {
data[depth].forEach(el => {
dict[el] = recursive(depth+1)
})
}
return dict
}
return recursive();
}
arrToDict(data)
I have used a recursion for converting each index to object and then used Memoization for a more efficient solution
Base Case: When the index has gone out of bounds of current array
Recursive Case: recurse over the next index and assign the next objectified index as a value to the current key
//store the index which is already iterated/objectified
//this is just for LESS COMPUTATION and MORE EFFICIENCY
const memoizeObjectifiedIndex = {};
let data = [['a', 'b'],['c', 'd'],['e', 'f', 'g', 'h']];
//basic recursive approach
function createObject(data,index){
//base case,
//if the index is just outside the length of array,
//here that index=3, since array is 0 indexed and last index is 2
if(index === data.length) return 0;
//check in memoized object if current index is already objectfied
if(memoizeObjectifiedIndex[index]){
//you can check the hits when this condition is true and COMPUTATION is saved
// console.log("Found for index ", index);
return memoizeObjectifiedIndex[index];}
const obj={};
data[index].forEach((key) => {
//assign the next objectified index as value to current key
obj[key] = createObject(data,index+1);
})
//store the object for current index for future use
memoizeObjectifiedIndex[index] = obj;
return obj;
}
console.log(createObject(data,0))
Note: You can see better output by copying and running this code in browser console.
Is there a simple way in javascript to take a flat array and convert into an object with the even-indexed members of the array as properties and odd-indexed members as corresponding values (analgous to ruby's Hash[*array])?
For example, if I have this:
[ 'a', 'b', 'c', 'd', 'e', 'f' ]
Then I want this:
{ 'a': 'b', 'c': 'd', 'e': 'f' }
The best I've come up with so far seems more verbose than it has to be:
var arr = [ 'a', 'b', 'c', 'd', 'e', 'f' ];
var obj = {};
for (var i = 0, len = arr.length; i < len; i += 2) {
obj[arr[i]] = arr[i + 1];
}
// obj => { 'a': 'b', 'c': 'd', 'e': 'f' }
Is there a better, less verbose, or more elegant way to do this? (Or I have just been programming in ruby too much lately?)
I'm looking for an answer in vanilla javascript, but would also be interested if there is a better way to do this if using undercore.js or jQuery. Performance is not really a concern.
Pretty sure this will work and is shorter:
var arr = [ 'a', 'b', 'c', 'd', 'e', 'f' ];
var obj = {};
while (arr.length) {
obj[arr.shift()] = arr.shift();
}
See shift().
var arr = [ 'a', 'b', 'c', 'd', 'e', 'f' ];
var obj = arr.reduce( function( ret, value, i, values ) {
if( i % 2 === 0 ) ret[ value ] = values[ i + 1 ];
return ret;
}, { } );
If you need it multiple times you can also add a method to the Array.prototype:
Array.prototype.to_object = function () {
var obj = {};
for(var i = 0; i < this.length; i += 2) {
obj[this[i]] = this[i + 1];
}
return obj
};
var a = [ 'a', 'b', 'c', 'd', 'e', 'f' ];
a.to_object(); // => { 'a': 'b', 'c': 'd', 'e': 'f' }
You could first chunk your array into groups of two:
[['a', 'b'], ['c', 'd'], ['e', 'f']]
so that is is in a valid format to be used by Object.fromEntries(), which will build your object for you:
const chunk = (arr, size) => arr.length ? [arr.slice(0, size), ...chunk(arr.slice(size), size)] : [];
const arr = ['a', 'b', 'c', 'd', 'e', 'f'];
const res = Object.fromEntries(chunk(arr, 2));
console.log(res); // {a: "b", c: "d", e: "f"}
With underscore.js and lodash, you don't need to implement the chunk() method yourself, and can instead use _.chunk(), a method built into both libraries. The full lodash equivalent of the above would be:
// lodash
> _.fromPairs(_.chunk(arr, 2));
> {a: "b", c: "d", e: "f"}
Using _.fromPairs provides better browser support, so if using lodash, it is preferred over Object.fromEntries()
Similarly, we can use _.object() if you're using underscore.js to build the object:
// underscore.js
> _.object(_.chunk(arr, 2));
> {a: "b", c: "d", e: "f"}
I have list of array associated with object. The requirement is to make a new key that will have all key value in there.
var obj = {
all:[
obj.one,
obj.two
],
one:[
'a',
'b',
'c'
],
two:[
'd',
'e',
'f',
'g'
]
}
//Wanted result
var obj = {
all:[
'a',
'b',
'c'
'd',
'e',
'f',
'g'
],
one:[
'a',
'b',
'c'
],
two:[
'd',
'e',
'f',
'g'
]
}
function accumulateKeyValues(obj){
var accumulated = [];
for(var key in obj)
accumulated = accumulated.concat(obj[key]);
obj["all"] = accumulated;
}
EXAMPLE:
function accumulateKeyValues(obj){
var accumulated = [];
for(var key in obj)
accumulated = accumulated.concat(obj[key]);
obj["all"] = accumulated;
}
var obj = {
one:[
'a',
'b',
'c'
],
two:[
'd',
'e',
'f',
'g'
]
};
accumulateKeyValues(obj);
console.log(obj);
You could get all keys of the object and take all values for a new array.
var obj = { one:['a', 'b', 'c'], two: ['d', 'e', 'f', 'g'] };
obj.all = Object.keys(obj).reduce(function (r, k) {
return r.concat(obj[k]);
}, []);
console.log(obj);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Traversal of an object is easy, but I found it hard to figure out the traversal paths for myself.
For example, here we have data like below:
data = {
a: 'A',
b: {
d: [
'F',
'G'
],
e: 'D'
},
c: 'C'
}
I want to output the traversal paths like this:
['a']
['b', 'd', 0]
['b', 'd', 1]
['b', 'e']
['c']
How do I write the algorithm?
function rec(currentObject, path) {
if (typeof currentObject !== "string" && currentObject.length) {
for (var i = 0; i < currentObject.length; i += 1) {
rec(currentObject[i], path.concat(i));
}
} else if (typeof currentObject === "object") {
for (var item in currentObject) {
rec(currentObject[item], path.concat(item))
}
} else {
console.log(path);
}
}
rec(data, []);
Output
[ 'a' ]
[ 'b', 'd', 0 ]
[ 'b', 'd', 1 ]
[ 'b', 'e' ]
[ 'c' ]
Is there a simple way in javascript to take a flat array and convert into an object with the even-indexed members of the array as properties and odd-indexed members as corresponding values (analgous to ruby's Hash[*array])?
For example, if I have this:
[ 'a', 'b', 'c', 'd', 'e', 'f' ]
Then I want this:
{ 'a': 'b', 'c': 'd', 'e': 'f' }
The best I've come up with so far seems more verbose than it has to be:
var arr = [ 'a', 'b', 'c', 'd', 'e', 'f' ];
var obj = {};
for (var i = 0, len = arr.length; i < len; i += 2) {
obj[arr[i]] = arr[i + 1];
}
// obj => { 'a': 'b', 'c': 'd', 'e': 'f' }
Is there a better, less verbose, or more elegant way to do this? (Or I have just been programming in ruby too much lately?)
I'm looking for an answer in vanilla javascript, but would also be interested if there is a better way to do this if using undercore.js or jQuery. Performance is not really a concern.
Pretty sure this will work and is shorter:
var arr = [ 'a', 'b', 'c', 'd', 'e', 'f' ];
var obj = {};
while (arr.length) {
obj[arr.shift()] = arr.shift();
}
See shift().
var arr = [ 'a', 'b', 'c', 'd', 'e', 'f' ];
var obj = arr.reduce( function( ret, value, i, values ) {
if( i % 2 === 0 ) ret[ value ] = values[ i + 1 ];
return ret;
}, { } );
If you need it multiple times you can also add a method to the Array.prototype:
Array.prototype.to_object = function () {
var obj = {};
for(var i = 0; i < this.length; i += 2) {
obj[this[i]] = this[i + 1];
}
return obj
};
var a = [ 'a', 'b', 'c', 'd', 'e', 'f' ];
a.to_object(); // => { 'a': 'b', 'c': 'd', 'e': 'f' }
You could first chunk your array into groups of two:
[['a', 'b'], ['c', 'd'], ['e', 'f']]
so that is is in a valid format to be used by Object.fromEntries(), which will build your object for you:
const chunk = (arr, size) => arr.length ? [arr.slice(0, size), ...chunk(arr.slice(size), size)] : [];
const arr = ['a', 'b', 'c', 'd', 'e', 'f'];
const res = Object.fromEntries(chunk(arr, 2));
console.log(res); // {a: "b", c: "d", e: "f"}
With underscore.js and lodash, you don't need to implement the chunk() method yourself, and can instead use _.chunk(), a method built into both libraries. The full lodash equivalent of the above would be:
// lodash
> _.fromPairs(_.chunk(arr, 2));
> {a: "b", c: "d", e: "f"}
Using _.fromPairs provides better browser support, so if using lodash, it is preferred over Object.fromEntries()
Similarly, we can use _.object() if you're using underscore.js to build the object:
// underscore.js
> _.object(_.chunk(arr, 2));
> {a: "b", c: "d", e: "f"}