Create Dynamically Nested Objects with loops - javascript

I wanna create a nested object dynamically. I can create it hard coded. Is it possible to do this with a loop ?
result = {}
keys = ["a", "b", "c", "d"]
result[keys[0]] = {}
result[keys[0]][keys[1]] = {}
result[keys[0]][keys[1]][keys[2]] = {}
result[keys[0]][keys[1]][keys[2]][keys[3]] = "cool"
I want to pass an integer for example if it is "3", this should created an object like:
result = {
"a": {
"b": {
"c": "cool"
}
}
}
If it is 4, :
result = {
"a": {
"b": {
"c": {
"d": "cool"
}
}
}
}
So on ...
edit:
I am also checking result object, in order to create this nested structure. If there is not any field yet, I simply create the object.
Using this structure to group data.
Any chance to check these dynamically ?
if (!result[keys[0]])
if (!result[keys[0]][keys[1]])
if (!result[keys[0]][keys[1]][keys[2]])

You can use reduceRight() for this. It just starts from the inside at the last item in the keys list and works its way out starting with "cool":
let keys = ["a", "b", "c", "d"]
let limit = 3
let result = keys.reduceRight((obj, key) => ({[key]: obj}), "cool")
console.log(result)
To limit where the object stops you can iterate over a slice of the keys. For example:
let keys = ["a", "b", "c", "d"]
let start = 0
let stop = 3 // slices are don't inlcude the last item, so this will stop at index 2
let result = keys.slice(start, stop).reduceRight((obj, key) => ({
[key]: obj
}), "cool")
console.log(result)

simple for-loop solution.
let result = {}
let keys = ["a", "b", "c", "d"]
let depth=3;
let current = result
for(let i=0;i<depth;++i){
let key = keys[i]
if(i == depth-1) current[key] = 'cool'
else current = current[key] = {}
}
console.log(result)

If you like to add to a given object a new property, you could reduce the keys with the object and take default objects for not given keys. At the end assign the value.
function setValue(object, path, value, limit) {
var keys = path.slice(0, limit),
last = keys.pop();
keys.reduce((o, k) => o[k] = o[k] || {}, object)[last] = value;
return object;
}
var result = { foo: 42 },
keys = ["a", "b", "c", "d"];
setValue(result, keys, 'cool');
console.log(result);
setValue(result, keys, 'cool', 3);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

short and simple using reduce
let object1= {}
keys = ['a', 'b', 'c']
keys.reduce((prev,curr,i)=>{
prev[curr] = {}
return prev[curr]
}, object1)
log(object1)
// {
// a:{
// b:{
// c:{}
// }
// }
// }

I'd use a reducer, along with some basic tests to help me out:
https://youtu.be/D6zLI8zrfVs
https://gist.github.com/brianswisher/2ce1ffe3ec08634f78aacd1b7baa31f9

Related

Find nested array by value

I've searched high and low for an answer to this, but nothing.
I have a nested array and want to find it by exact value but can't seem to get it to work:
let rowLetters = ["A","B","C",["D","E"],"F"];
for(n=0;n<rowLetters.length;n++){
if(rowLetters[n] === ["D","E"]){
console.log("Found");
}
console.log(rowLetters[n]);
}
Console Output:
"A"
"B"
"C"
["D","E"] // <-- There it is..
"F"
What am I doing wrong?
You need to check
if item is an array,
if item has the same length as the wanted value array and
if the values of the arrays are equal.
let rowLetters = ["A", "B", "C", ["D", "E"], "F"],
search = ["D", "E"];
for (const item of rowLetters) {
if (Array.isArray(item) && item.length === search.length && search.every((v, i) => item[i] === v)) {
console.log(item);
}
}
You can use filter with JSON.stringify()
let data = ["A", "B", "C", ["D", "E"], "F"];
let search = data.filter(ele => JSON.stringify(ele) == JSON.stringify(["D", "E"]));
if (search.length > 0) {
console.log("found")
}
Are you looking for something like find() mixed with Array.isArray()?
let rowLetters = ["A","B","C",["D","E"],"F"];
console.log(rowLetters.find(i => Array.isArray(i)))
You cannot compare an array to an array because you are comparing by a reference and not a value. You can however cast the value to a json string and compare, however, this requires exact order in both arrays.
let rowLetters = ["A","B","C",["D","E"],"F"];
for(let i of rowLetters){
if(JSON.stringify(i) === JSON.stringify(["D","E"])) {
console.log("Found");
}
}
You can use .find and JSON.stringify:
let rowLetters = ["A","B","C",["D","E"],"F"];
let arrayToFind = JSON.stringify(["D","E"])
let nestedArray = rowLetters.find( arr => JSON.stringify(arr) === arrayToFind );
console.log(nestedArray);
A better way to check if two arrays are equal would be using .every and .includes as follows:
let rowLetters = ["A","B","C",["D","E"],"F"];
const arraysAreEqual = (arr1, arr2) => {
if(arr1.length != arr2.length) return false;
return arr1.every( e => arr2.includes(e) );
}
const arrayToFind = ["D","E"];
let nestedArray = rowLetters.find( arr => arraysAreEqual(arr, arrayToFind) );
console.log(nestedArray);

Lodash _.flatMapDepth to return an array of objects deep level nested

i am trying to get an array from deep level nested structure, i was able to get one level. Even if i pass depth as 4 also i am not able to get.
any help is appreciated
input
let a = [{b: [{c: [{d: [{e: "name"}]}]}]}]
tried snippet
let output = _.flatMapDepth(a, 'e', 3);
getting empty array
i need to get as below, using lodash
output = [{e: "name"}]
any help is appreciated
better use _().flatMap
let a = [{b: [{c: [{d: [{e: "name"}]}]}]}];
console.log(a);
let output1 = _(a).flatMap('b').flatMap('c').flatMap('d').value();
console.log(output1); // [ { e: 'name' } ]
let output2 = _(a).flatMap('b').flatMap('c').flatMap('d').flatMap('e').value();
console.log(output2); // [ 'name' ]
This one goes through the nested objects and resolves them!
From the Lodash docs:
_.flatMapDepth
// _.flatMapDepth(collection, [iteratee=_.identity], [depth=1])
function duplicate(n) {
return [[[n, n]]];
}
_.flatMapDepth([1, 2], duplicate, 2);
// => [[1, 1], [2, 2]]
and _.identity
// _.identity(value)
var object = { 'a': 1 };
console.log(_.identity(object) === object);
// => true
I think this should help.
If this does not help. I have written a JavaScript solution to your problem:
let a = [{
b: [{
c: [{
d: [{
e: "name"
}]
}]
}]
}, {
b: [{
c: [{
d: [{
e: "age"
}]
}]
}]
}];
function getDeepKeys(arr, key, maxDepth = 8) {
let res = [];
let depth = 0;
for (let i = 0; i < arr.length; i++) {
const obj = arr[i];
if (typeof obj !== "object" || obj == null) {
continue;
}
for (const k in obj) {
if (obj.hasOwnProperty(k)) {
const element = obj[k];
if (k === key) {
res.push(obj);
// can also be res.push(element)
// if you want the contents of obj to be added to the resulting array
continue;
} else if (depth <= maxDepth) {
res = getDeepKeys(element, key).concat(res);
depth++;
}
}
}
}
return res;
}
let keys = getDeepKeys(a, "e");
console.log(keys);
Be careful, though. If there's an object without the e key, you will get an infinite loop error. Therefor I created the depth and maxDepth variable. You can adjust this value like so: getDeepKeys(a, "e", 12); (now the maxDepth equals 12 instead of the default value of 10).

how to convert multidimention array to arr in object

can you explain the logic how to convert this 2d array to array and in array has objects, here is the input :
const arr = [
["Tony", "a", "b"],
["Sara", "c", "z"]
];
how to convert them to be:
obj = [
{
name:"Tony",
first:"a",
second:"b"
},
{
name:"Sara",
first:"c",
second:"z"
}
]
should we create 2 objects temporary and are ? to put them in the array,
and how about looping? can we just use one-time looping?
and how if that 2d array length is not same with ther 2d on the first or the second,
i do love to know the method, with explaning if you dont mind,
and i do like you all dont use ES6 for this :), so i know the logic
Use Array.map(). In the map's callback, extract the values to variables, using array destructuring, than create the object with shorthand property names:
const arr = [["Tony", "a", "b"], ["Sara", "c", "z"]];
const result = arr.map(([name, first, second]) => ({
name,
first,
second
}));
console.log(result);
And if you don't want to use Array.map(), you can build one using a for...of loop:
const map = (arr, cb) => {
const r = [];
for(const item of arr) {
r.push(cb(item));
}
return r;
};
const arr = [["Tony", "a", "b"], ["Sara", "c", "z"]];
const result = map(arr, ([name, first, second]) => ({
name,
first,
second
}));
console.log(result);
You can use .map() with some array destructuring:
const arr = [
["Tony", "a", "b"],
["Sara", "c", "z"]
];
const result = arr.map(([name, first, second]) => ({name, first, second}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Alternatively, you can use a simple for loop:
const arr = [
["Tony", "a", "b"],
["Sara", "c", "z"]
];
const result = [];
for(var i = 0; i < arr.length; i++) {
result.push({
name: arr[i][0],
first: arr[i][1],
second: arr[i][2]
});
}
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
var arr = [["Tony", "a", "b"], ["Sara", "c", "z"]];
function parseData(input){
var output = [];
for(var i = 0; i< input.length ; i++){
output.push({name:input[i][0],first:input[i][1],second:input[i][2]})
}
return output;
}
console.log(parseData(arr));
EDIT - Explanation
As the input is structured as 2D array with inner array is of fixed length of 3 and outer is of non-negative ( >= 0 ). Iterate over the outer array using for loop and inner array using the index number and store the result in output array.
JsFiddle demo - https://jsfiddle.net/53umf8rv/
Without Map:
const arr = [
["Tony", "a", "b"],
["Sara", "c", "z"]
];
obj=[];
for(innerArr of arr)
{
obj.push({"name":innerArr[0],"first":innerArr[1],"second":innerArr[2]});
}
console.log(obj);
Without using higher order functions you can achieve this using a for of loop:
const arr = [
["Tony", "a", "b"],
["Sara", "c", "z"]
];
let obj_arr = [];
for(inner_arr of arr) {
obj_arr = [...obj_arr, {name: inner_arr[0], first: inner_arr[1], second: inner_arr[2]}];
}
console.log(obj_arr);
You could use an outer for ... of statement, which iterates the items of the array and iterate the inner array by using a classic for statement with an index variable, which is used to get the corresponding key value as well.
For each inner iteration take a new property for the temporary object and assign a value. At the end of the inner iteration push the temporary object to the result set.
function convert(array, keys) {
var result = [],
items,
i,
temp;
for (items of array) {
temp = {};
for (i = 0; i < items.length; i++) {
temp[keys[i]] = items[i];
}
result.push(temp);
}
return result;
}
console.log(convert([["Tony", "a", "b"], ["Sara", "c", "z"]], ["name", "first", "second"]));

How to add an element to an array only if a condition is fulfilled

There is an arrow function creating an array like:
const myFunction = () => ["a", "b", "c"];
I want to add an argument to it that must add another element if the argument is true.
Like here:
const myFunction = (arg) => ["a", "b", "c", arg ? "d" : null];
the problem with this solution is that is still adds a null element if arg !== true but I want to don't add anything in this case.
You can use array spread. According to the value of arg an empty array or an array that contains d will be spreaded into the result array:
const myFunction = (arg) => ["a", "b", "c", ...arg ? ['d'] : []];
console.log(JSON.stringify(myFunction(true))); // ["a","b","c","d"]
console.log(JSON.stringify(myFunction())); // ["a","b","c"]
You could use concat:
const myFunction = (arg) => ["a", "b", "c"].concat(arg ? ["d"] : []);
console.log(myFunction(true));
console.log(myFunction(false));
You can use Array push().
const myFunction = (arg) => {
const arr = ["a", "b", "c"];
if (arg) arr.push("d");
return arr;
};
console.log(myFunction(true));
console.log(myFunction(false));
You can make the function a bit longer,
create a temporary array,
append the element to the temporary array if required,
and return the temporary array when done
const myFunction = (arg) => {
var tempArray = ["a", "b", "c"];
if (arg) {
tempArray.push("d");
}
return tempArray;
};
console.log(myFunction(true) + "");
console.log(myFunction(false) + "");
const myFunction = (arg) => {
ret = ['a', 'b', 'c']
return arg === true ? ret.concat('d') : ret;
}
In other solutions you have arg ? instead of arg === true ?.
If you want myFunction to return array with 'd' ONLY for arg = true,then you shoulu use mine solution. If you want it to return 'd' for, for example, arg = 17, but not to return it for arg = 0, then use others solution.
You can do this also :
const myMethod = (arg) => {
var tempArray = ["item 1", "item 2", "item 3"];
!arg || tempArray.push("item 4");
return tempArray;
};
console.log(myMethod(false));
console.log(myMethod(true));
Ori has the right answer. Use that for all the modern browsers. If for some reason you're still stuck on an older browser -
["a", "b", "c"].concat(arg ? 'd' : [])
If you store your array in a variable you can make it like this:
const arr = ["a", "b", "c"];
const myFunction = arg => arg === true ? [...arr, "d"] : arr;
console.log(myFunction(true));
console.log(myFunction());

split an array into two based on a index in javascript

I have an array with a list of objects. I want to split this array at one particular index, say 4 (this in real is a variable). I want to store the second part of the split array into another array. Might be simple, but I am unable to think of a nice way to do this.
Use slice, as such:
var ar = [1,2,3,4,5,6];
var p1 = ar.slice(0,4);
var p2 = ar.slice(4);
You can use Array#splice to chop all elements after a specified index off the end of the array and return them:
x = ["a", "b", "c", "d", "e", "f", "g"];
y = x.splice(3);
console.log(x); // ["a", "b", "c"]
console.log(y); // ["d", "e", "f", "g"]
use slice:
var bigOne = [0,1,2,3,4,5,6];
var splittedOne = bigOne.slice(3 /*your Index*/);
I would recommend to use slice() like below
ar.slice(startIndex,length);
or
ar.slice(startIndex);
var ar = ["a","b","c","d","e","f","g"];
var p1 = ar.slice(0,3);
var p2 = ar.slice(3);
console.log(p1);
console.log(p2);
const splitAt = (i, arr) => {
const clonedArray = [...arr];
return [clonedArray.splice(0, i), clonedArray];
}
const [left, right] = splitAt(1, [1,2,3,4])
console.log(left) // [1]
console.log(right) // [2,3,4]
const [left1, right1] = splitAt(-1, [1,2,3,4])
console.log(left1) // []
console.log(right1) // [1,2,3,4]
const [left2, right2] = splitAt(5, [1,2,3,4])
console.log(left1) // [1,2,3,4]
console.log(right1) // []
Some benefits compared to other solutions:
You can get the result with a one liner
When split index is underflow or overflow, the result is still correct. slice will not behave correctly.
It does not mutate the original array. Some splice based solutions did.
There is only 1 splice operation, rather than 2 slice operations. But you need to benchmark to see if there is actual performance difference.
You can also use underscore/lodash wrapper:
var ar = [1,2,3,4,5,6];
var p1 = _.first(ar, 4);
var p2 = _.rest(ar, 4);
Simple one function from lodash:
const mainArr = [1,2,3,4,5,6,7]
const [arr1, arr2] = _.chunk(mainArr, _.round(mainArr.length / 2));
const splitArrayByIndex = (arr, index) => {
if (index > 0 && index < arr.length) {
return [arr.slice(0, index), arr.slice(-1 * (arr.length - index))]
}
}
const input = ['a', 'x', 'c', 'r']
const output = splitArrayByIndex(input, 2)
console.log({ input, output })

Categories

Resources