Calling multiple nested object recursively giving undefined - javascript

I wrote a simple algo whose job is to find the corresponding name where the profession is teacher.
The given code calls the function recursively until the given result is achieved.
On executing the code, I am getting the final output is undefined. where as I was expecting the name to be ishan.
Can someone help me in diagnosing the problem in my algo?
//Accessing infitely nested Array
// Infinitely nested Array
const infiniteArray = [
{
name: "Jack",
age: "98",
profession: "doctor",
children: [
{
name: "Varun",
age: "80",
profession: "scientist",
children: [
{
name: "Ishan",
age: "62",
profession: "teacher"
}
]
}
]
}
];
const accessNestedObject = (infiniteArray) => {
return infiniteArray.forEach(element => {
if (element['profession'] === 'teacher') {
console.log(element.name)
return element.name
} else {
console.log(element["children"])
return accessNestedObject(element["children"])
}
});
}
const result = accessNestedObject(infiniteArray)
console.log(result)

You are getting undefined because that's the expected return value of Array#forEach.
You have to declare a variable that will store the final result of your loop.
//Accessing infitely nested Array
// Infinitely nested Array
const infiniteArray = [
{
name: "Jack",
age: "98",
profession: "doctor",
children: [
{
name: "Varun",
age: "80",
profession: "scientist",
children: [
{
name: "Ishan",
age: "62",
profession: "teacher"
}
]
}
]
}
];
const accessNestedObject = (infiniteArray) => {
let result = null;
infiniteArray.forEach(element => {
if (element.profession === 'teacher') {
result = element.name;
} else {
result = accessNestedObject(element.children);
}
});
return result;
}
const result = accessNestedObject(infiniteArray);
console.log(result);

Related

Looping through an array of objects and filter using a key

I have a nested array of objects as below
const person = [
{
firstName: 'Alex',
secondName: 'Lexi',
address: [
{ place: 'California'},
{ pin: 1233 }
]
},
{
firstName: 'Max',
secondName: 'Lui',
address: [
{ place: 'New York' },
{ pin: 3455 }
]
}
]
I would like to loop through the array and get the address object corresponding to the firstName 'Max'.
I can use this data to generate links and their sublinks (on clicking the links)
I have attempted the following
const renderPerson = () => {
person.forEach((item) => {
return item.firstName;
})
}
const renderAddress = () => {
person.forEach((item) => {
if(item.firstName === "Max" {
return item.address;
}
}
}
Then
const html = `<li>${renderPerson()}</li>`;
document.getElementById("display").innerHTML = html;
But it returns undefined. How can solve it?
You should look up array find().
It would look something like this:
const person = [
{
firstName: "Alex",
secondName: "Lexi",
address: [{ place: "California" }, { pin: 1233 }],
},
{
firstName: "Max",
secondName: "Lui",
address: [{ place: "New York" }, { pin: 3455 }],
},
];
const myPerson = person.find(p => p.firstName === "Max");
console.log(myPerson?.address); // [{place: "New York"}, {pin: 3455}]

Lodash how to group array of objects by array of values?

I want to group an array of objects based on a property which has an array of values,and i want to return a group for each individual value,not only for the whole array.
For example :
let crew = [
{
name:"john",
job :["electrician","carpenter"]
},
{
name: "bill",
job: ["electrician"]
},
{
name: "mark",
job: [ "carpenter"]
}
]
let groupedCrew = _.groupBy(crew,"job")
console.log(groupedCrew)
/*
carpenter:
[
{
job:
[
carpenter
],
name:
"mark"
}
],
electrician:
[
{
job:
[
"electrician"
],
name:
"bill"
}
],
electrician, carpenter:
[
{
job:
[
"electrician",
"carpenter"
],
name:
"john"
}
]
}
*/
In this example i want "john" to also appear in "electrician" group.
Any ideas ?
Once again let's group something using reduce
Here's the basic structure (plus the solution)
let crew = [{
name: "john",
job: ["electrician", "carpenter"]
},
{
name: "bill",
job: ["electrician"]
},
{
name: "mark",
job: ["carpenter"]
}
];
var obj = crew.reduce(function(agg, item) {
// grouping logic below this line
item.job.forEach(function(job) {
agg[job] = agg[job] || []
// agg[job].push (item);
// lets push only name so we can see output
agg[job].push(item.name)
})
// grouping logic above this line
return agg
}, {})
console.log(obj)
use custom .reduce() function
there is no need for lodash
const crew = [
{
name: 'john',
job: ['electrician', 'carpenter'],
},
{
name: 'bill',
job: ['electrician'],
},
{
name: 'mark',
job: ['carpenter'],
},
];
const groupedCrew = crew.reduce((groupedCrew, person) => {
person.job.forEach(job => {
if (!groupedCrew[job]) groupedCrew[job] = [];
groupedCrew[job].push(person);
});
return groupedCrew;
}, {});
console.log(JSON.stringify(groupedCrew, null, 4));

JS get all the arrays within an array

I have an object that looks like the following:
const test = {
leagues: [
{
timezone: "GMT",
date: "1/2/2",
premierLeague: [
{ name: "Liverpool", age: 1892 },
{ name: "Manchester Utd", age: 1878 }
],
laLiga: [
{
team: "Real Madrid",
stadium: "Bernabeu"
},
{
team: "Barcelona",
stadium: "Camp Nou"
}
]
}
]
};
and I want the result to look like
const result = [
{ name: "Liverpool", age: 1892 },
{ name: "Manchester Utd", age: 1878 },
{
team: "Real Madrid",
stadium: "Bernabeu"
},
{
team: "Barcelona",
stadium: "Camp Nou"
}
];
I have tried to use flat() but am having trouble getting the arrays within the leagues. The result will be dynamic so I need to be able to get all the arrays within leagues. Can anyone point me in the correct direction to do this?
If your object structure doesn't go an deeper than that, this long one-liner should work:
const result = test.leagues.reduce((arr, obj) => Object.values(val).reduce((innerArr, val) => Array.isArray(val) ? innerArr.concat(val) : innerArr, arr), []);
Somewhat ungolfed:
const result = test.leagues.reduce((arr, obj) => {
return Object.values(val).reduce((innerArr, val) => {
return Array.isArray(val)
? innerArr.concat(val)
: innerArr
}, arr);
}), []);
You might be looking for
const result = test.leagues.flatMap(league =>
Object.values(league).filter(Array.isArray).flat()
);
This sounds weird, you'll end up with objects of different shapes in the same array. I'm not sure how you'll deal with that.
It looks like you're trying to concatenate every value of test.leagues that is itself an array.
const test = {
leagues: [{
timezone: "GMT",
date: "1/2/2",
premierLeague: [{
name: "Liverpool",
age: 1892
},
{
name: "Manchester Utd",
age: 1878
}
],
laLiga: [{
team: "Real Madrid",
stadium: "Bernabeu"
},
{
team: "Barcelona",
stadium: "Camp Nou"
}
]
}]
};
const output = [];
for (const league of test.leagues) {
for (const key in league) {
if (Array.isArray(league[key])) {
// Push each element in `league[key]` onto `output`
// so we don't have to flatten it later
output.push(...league[key]);
}
}
}
console.log({
output
});
Well, I'm also adding my 2 cents over here. I do agree with everyone else. See if this works:
const test = {
leagues: [
{
timezone: "GMT",
date: "1/2/2",
premierLeague: [
{ name: "Liverpool", age: 1892 },
{ name: "Manchester Utd", age: 1878 }
],
laLiga: [
{
team: "Real Madrid",
stadium: "Bernabeu"
},
{
team: "Barcelona",
stadium: "Camp Nou"
}
]
}
]
};
let finalArray = [];
function recursiveArr(obj, arrayToPush) {
for(const [key, val] of Object.entries(obj)) {
if(Array.isArray(obj[key])) {
arrayToPush.push(obj[key]);
continue;
}
const type = typeof obj[key];
if(type === "object") {
recursiveArr(obj[key], arrayToPush);
}
}
}
recursiveArr(test.leagues, finalArray);
console.log(finalArray.flat())

push value duplicate into new array

I have array of object like this
const data = [
{
name: "John",
transaction: "10/10/2010",
item: "Bag"
},
{
name: "Steven",
transaction: "31/10/2020",
item: "Shoe"
},
{
name: "John",
transaction: "18/06/2019",
item: "Sock"
}
]
you can see that the name of object in that array has duplicate name but different transaction
and then I want the result like this :
const result = [
{
name: "John",
transactions: [
{
date: "10/10/2010",
item: "Bag"
},
{
date: "18/06/2019",
item: "Sock"
}
]
},
{
name: "Steven",
transactions: [
{
date: "31/10/2020",
item: "Shoe"
}
]
},
]
so the new array recored the new transactions of the same person
the code for this is:
const data = [
{
name: "John",
transaction: "10/10/2010",
item: "Bag"
},
{
name: "Steven",
transaction: "31/10/2020",
item: "Shoe"
},
{
name: "John",
transaction: "18/06/2019",
item: "Sock"
}
]
let Transactions = []
data.forEach(data => {
Transactions.some(t => {
if(t.name === data.name){
t.transactions.push({date:data.transaction,item:data.item})
return;
}
})
Transactions.push({
name:data.name,
transactions:[
{date:data.transaction,item:data.item}
]
})
console.log(Transactions);
})
array.some is better than forEach loop i think.so decided to stick with that.
Please try the following example
const data = [
{
name: "John",
transaction: "10/10/2010",
item: "Bag",
},
{
name: "Steven",
transaction: "31/10/2020",
item: "Shoe",
},
{
name: "John",
transaction: "18/06/2019",
item: "Sock",
},
];
const output = data.reduce((previousValue, { name, transaction, item }) => {
const index = previousValue.findIndex((entry) => entry.name === name);
if (index === -1) {
previousValue = [
...previousValue,
{
name: name,
transactions: [{ date: transaction, item }],
},
];
} else {
previousValue[index].transactions = previousValue[
index
].transactions.concat({
date: transaction,
item,
});
}
return previousValue;
}, []);
console.dir(output, { depth: null, color: true });
See
Array.prototype.reduce()
Array.prototype.concat()
Array.prototype.findIndex()
a simple reduce do that
const data =
[ { name: 'John', transaction: '10/10/2010', item: 'Bag' }
, { name: 'Steven', transaction: '31/10/2020', item: 'Shoe' }
, { name: 'John', transaction: '18/06/2019', item: 'Sock' }
]
const result = data.reduce((a,{name,transaction:date,item})=>
{
let x = a.find(e=>e.name===name)
if (!x)
{
let n = a.push({name, transactions:[]}) -1
x = a[n]
}
x.transactions.push({date,item})
return a
},[])
console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0; }
shorter version
const result = data.reduce((a,{name,transaction:date,item})=>
{
let x = a.find(e=>e.name===name) || (a[a.push({name, transactions:[]}) -1])
x.transactions.push({date,item})
return a
},[])
You could do that in a functional way to make it readable, below worked solution is using ramdajs
const data = [
{
name: 'John',
transaction: '10/10/2010',
item: 'Bag'
},
{
name: 'Steven',
transaction: '31/10/2020',
item: 'Shoe'
},
{
name: 'John',
transaction: '18/06/2019',
item: 'Sock'
}
]
const result = pipe(
groupBy(obj => obj.name),
mapObjIndexed((groupObjs, groupName) => ({
name: groupName,
transactions: map(
groupObj => ({
date: groupObj.transaction,
item: groupObj.item
}),
groupObjs
)
})),
values
)(data)
console.log(result)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>
<script>const { groupBy, mapObjIndexed, pipe, map, values } = R</script>
Here is the link to the ramdajs doc
How about using lodash's _.groupBy() function?
const data = [
{
name: "John",
transaction: "10/10/2010",
item: "Bag",
},
{
name: "Steven",
transaction: "31/10/2020",
item: "Shoe",
},
{
name: "John",
transaction: "18/06/2019",
item: "Sock",
}
]
const result = _.groupBy(data, "name")
console.log(result)
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.15/lodash.min.js"></script>

Mapping object keys and values into another object

How would you implement a mapping function, from an object keys to another object that has a property with that specific key, preserving the object structure
For example, given this input object:
{
person: {
first_name: "fn",
last_name: "ln",
address: {
city: "c",
street: "s",
},
movies: [
{
title: "movie1"
},
{
title: "movie2"
}
]
}
}
the result should be:
{
name: "person",
children: [
{
name: "first_name"
},
{
name: "last_name"
},
{
name: "address",
children: [
{
name: "city"
},
{
name: "street"
}
]
},
{
name: "movies",
children: [
{
name: "title"
}
]
}
]
}
I've tried with some recursive object traversals but the implementation was really ugly. I feel that there is an easier way to handle it
You can do that using recursion. Create a function which takes an entry as input. Entry is an array of two element which contains key value pair for an object. [key,value]
const obj = { person: { first_name: "fn", last_name: "ln", address: { city: "c", street: "s", }, movies: [ { title: "movie1" }, { title: "movie2" } ] } }
function getKeys(entry){
let obj = {
name:entry[0],
children:[]
}
const { children } = obj
let val = entry[1]
for(let k in val){
if(Array.isArray(val[k])){
children.push(getKeys([k,val[k][1]]));
}
else if(typeof val[k] === "object"){
children.push(getKeys([k,val[k]]))
}
else children.push({name:k})
}
if(!obj.children.length) delete obj.children;
return obj
}
console.log(getKeys(Object.entries(obj)[0]))

Categories