Mapping object keys and values into another object - javascript

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]))

Related

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));

Need to return existing data structure with update objects

I wanted to update the object when it pass the check but getting different result then expected.
What is preferred way to get the expected?
This the sample data:
var abc = [
{
type: "manager",
members: [{name: 'bob'}, {name: 'rob'}]
},
{
type: "clerk",
members: [{name: 'foo'}, {name: 'bar'}]
}
];
Using this function :
function funn() {
return abc.map((cate) => {
return cate.members.map((mem) => {
if (mem.name === 'bob') {
mem['isBob'] = true;
}
return mem;
});
});
}
I wanted in this format (expected):
[
{
type: 'manager',
members: [{ name: 'bob', isBob: true }, { name: 'rob' }],
},
{
type: 'clerk',
members: [{ name: 'foo' }, { name: 'bar' }],
},
];
The actual is like this:
[
{
members: [{ name: 'bob' }, { name: 'rob' }],
},
{
members: [{ name: 'foo' }, { name: 'bar' }],
},
];
You just need to find the element in the members array and if it is present then add the isBob property.
var abc = [{
type: "manager",
members: [{
name: "bob"
}, {
name: "rob"
}],
},
{
type: "clerk",
members: [{
name: "foo"
}, {
name: "bar"
}],
},
];
const result = abc.map((obj) => {
const isExist = obj.members.find((o) => o.name === "bob");
if (isExist) isExist.isBob = true;
return obj;
});
console.log(result);
The output you showed for your code is not matching when i run it.
Here I have modified your code a bit:
Instead of returning an array from the outer .map(), I am returning an object which has a type and members category.
function funn() {
return abc.map((cate) => {
let cateMembers = cate.members.map((mem) => {
if (mem.name === 'bob') {
mem['isBob'] = true;
}
return mem;
});
return { type : cate.type , members : cateMembers};
});
}
Remember to check like this : console.log(funn())
You could do something like this:
var abc = [
{
type: "manager",
members: [{name: 'bob'}, {name: 'rob'}]
},
{
type: "clerk",
members: [{name: 'foo'}, {name: 'bar'}]
}
];
function funn() {
abc.forEach(el => {
el.members.forEach(el2 => {
if(el2.name === 'bob') el2.isBob = true
})
})
return abc
}
console.log(funn())

Flatten an object of child arrays

I have Object which look like
list:{
dynamicPerson: [
0: {
name:"Andrew",
company:"xCompany"
},
1: {
name:"Joseph",
company:"zCompany"
}
]
dynamicPerson2: [
0: {
name:"Andrew",
company:"xCompany"
},
1: {
name:"Joseph",
company:"zCompany"
}
]
}
I want it like
List:[
0: {
name:"Andrew",
company:"xCompany"
},
1: {
name:"Joseph",
company:"zCompany"
},
2: {
name:"Andrew",
company:"xCompany"
},
3: {
name:"Joseph",
company:"zCompany"
}
]
Note DynamicPerson is run time generated key.
You can iterate trought the keys of the object. Map the arrays to an array of array and then flatten it.
let flattened = Object.keys(list).map(key => list[key]).flat();
I modified your input slightly, because it was not syntactically right.
let list = {
dynamicPerson: [
{
name: "Andrew",
company: "xCompany"
},
{
name: "Joseph",
company: "zCompany"
}
],
dynamicPerson2: [
{
name: "Andrew",
company: "xCompany"
},
{
name: "Joseph",
company: "zCompany"
}
]
};
let flattened = Object.keys(list)
.map(key => list[key])
.flat();
console.log(flattened);
You'll find it yourself by looking for: convert object to array-of-key-value-pairs
But maybe look at the snipped in here: how-to-convert-an-object-to-an-array-of-key-value-pairs can help you
You can use flatMap():
Object.values(list).flatMap(s => s);
An example:
let list ={
dynamicPerson: [
{
name:"Andrew",
company:"xCompany"
},
{
name:"Joseph",
company:"zCompany"
}
]
, dynamicPerson2: [
{
name:"Andrew",
company:"xCompany"
},
{
name:"Joseph",
company:"zCompany"
}
]
}
const result = Object.values(list).flatMap(s => s);
console.log(result);
You can also use Object.values() and flat:
let list = {
dynamicPerson: [{
name: "Andrew",
company: "xCompany"
},
{
name: "Joseph",
company: "zCompany"
}
],
dynamicPerson2: [{
name: "Andrew",
company: "xCompany"
},
{
name: "Joseph",
company: "zCompany"
}
]
}
let List = Object.values(list).flat();
console.log(List);

How to efficiently construct new object array using unique values from arrays of children inside an array of parent objects

The need is to take objects like this:
[ { "first":
{ "children" : [{ "name": "abc", "detail":"123"},
{ "name": "def", "detail":"456"}
]
}},
{ "second":
{ "children" : [{ "name": "ghi", "detail":"123"},
{ "name": "jkl", "detail":"456"}
]
}},
{ "third":
{ "children" : [{ "name": "mno", "detail":"123"},
{ "name": "pqr", "detail":"456"}
]
}},
{ "fourth":
{ "children" : [{ "name": "stu", "detail":"123"},
{ "name": "vwx", "detail":"456"}
]
}},
{ "fifth":
{ "children" : [{ "name": "yz", "detail":"123"},
{ "name": "abc", "detail":"456"}
]
}},
{ "sixth":
{ "children" : [{ "name": "def", "detail":"123"},
{ "name": "ghi", "detail":"456"}
]
}}
]
and then create a flattened array of unique values (options for a select) from the name field of the children that looks like this:
[{"value":"abc", "label":"abc"},
{"value":"def", "label":"def"},
{"value":"ghi", "label":"ghi"},
{"value":"jkl", "label":"jkl"},
{"value":"mno", "label":"mno"},
{"value":"pqr", "label":"pqr"},
{"value":"stu", "label":"stu"},
{"value":"vwx", "label":"vwx"},
{"value":"yz", "label":"yz"}
]
The code below is working, but it looks like it is inefficient because it appears to make many passes over the array:
[
...new Set(
[].concat.apply([], bases.map((base) => {
if (!base.children || base.children.length === 0) return;
return base.children}
)).map((child) => child.name)
)
].map((optName) => {return {value: optName, label: optName};})
If it is possible, how can this same result be achieved without as many iterations across the array.
Firstly, as a rule of thumb, you shouldn't worry too much about performance until you have a reason to do so.
Secondly, chaining the array prototype functions (e.g. map, forEach, filter) will require multiple iterations by design.
Thirdly, there's no reason to assume multiple iterations is slower than a single iteration if the work done within the iterations is the same anyways. I.e. incrementing an index and comparing it with an array length isn't going to be the bottleneck compared to pushing objects into arrays and check set entries.
Here's a (IMO) cleaner snippet to extract unique names from your array:
let bases = [{
children: [{
name: "abc",
detail: "123"
},
{
name: "def",
detail: "456"
}
]
}, {
children: [{
name: "abc" ,
detail: "123"
},
{
name: "xyz" ,
detail: "456"
}
]
},
{}
];
let output = bases
.flatMap(b => b.children || [])
.map(c => c.name)
.filter((v, i, a) => a.indexOf(v) === i) // filter unique values
.map(name => ({
value: name,
label: name,
}));
console.log(output);
Now if you really want to do all this in a single iteration, that too is possible, but harder to read:
let bases = [{
children: [{
name: "abc",
detail: "123"
},
{
name: "def",
detail: "456"
}
]
}, {
children: [{
name: "abc" ,
detail: "123"
},
{
name: "xyz" ,
detail: "456"
}
]
},
{}
];
let output = [];
let seenNames = {};
for (base of bases) {
if (!base.children)
continue;
for (child of base.children) {
let name = child.name;
if (seenNames[name])
continue;
seenNames[name] = true;
output.push({
value: name,
label: name,
});
}
}
console.log(output);
You could take Array#flatMap for getting a flat representation of data for using unique values and map new objects.
var data = [{ first: { children: [{ name: "abc", detail: "123" }, { name: "def", detail: "456" }] } }, { second: { children: [{ name: "ghi", detail: "123" }, { name: "jkl", detail: "456" }] } }, { third: { children: [{ name: "mno", detail: "123" }, { name: "pqr", detail: "456" }] } }, { fourth: { children: [{ name: "stu", detail: "123" }, { name: "vwx", detail: "456" }] } }, { fifth: { children: [{ name: "yz", detail: "123" }, { name: "abc", detail: "456" }] } }, { sixth: { children: [{ name: "def", detail: "123" }, { name: "ghi", detail: "456" }] } }],
result = Array.from(
new Set(data
.flatMap(Object.values)
.flatMap(({ children }) => children.map(({ name }) => name))
),
value => ({ value, label: value })
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Calling multiple nested object recursively giving undefined

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);

Categories