Related
I am looking the following example lodash - group and populate arrays.
However I need to group by multi times like following example. What should I do? Thanks
[
{ birthdate: "1993", day: "12", category: "a", name: "Ben" },
{ birthdate: "1993", day: "13", category: "a", name: "John" },
{ birthdate: "1993", day: "14", category: "b", name: "Larry" },
{ birthdate: "1994", day: "15", category: "", name: "Nicole" },
];
to
[
{
birthdate: "1993",
birthdateDetail: [
{
category: "a",
categoryDetail: [
{ day: "12", name: "Ben" },
{ day: "13", name: "John" },
],
},
{
category: "b",
categoryDetail: [{ day: "14", name: "Larry" }],
},
],
},
{
birthdate: "1994",
birthdateDetail: [{ category: "", day: "15", name: "Nicole" }],
},
];
You could groups with an abstract approach for nested grouping by taking an object which keeps the keys for each level as well as the wanted result structure.
This approach works for arbitrary nesting.
const
data = [{ birthdate: "1993", day: "12", category: "a", name: "Ben" }, { birthdate: "1993", day: "13", category: "a", name: "John" }, { birthdate: "1993", day: "14", category: "b", name: "Larry" }, { birthdate: "1994", day: "15", category: "", name: "Nicole" }],
groups = ['birthdate', 'category'],
getEmpty = () => ({ _: [] }),
result = data
.reduce((q, o) => {
groups
.reduce((r, k) => {
const v = o[k];
if (!v) return r;
if (!r[v]) r._.push({ [k]: v, [k + 'Detail']: (r[v] = getEmpty())._ });
return r[v];
}, q)
._
.push(o);
return q;
}, getEmpty())
._;
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You're in the right direction: using _.groupBy will help you to group your data, but since you need two levels of nesting, you will need to run it twice:
The top level is to group by birthdate
The second level is to group by category
Since _.groupBy returns an object, we will need to use Object.entries to transform the key-value pair into an array of objects that fit the shape you have intended.
At the second level, we will need to do a final iteration to ensure that we remove the category and birthday keys from the nested data.
See proof-of-concept below:
const data = [
{ birthdate: "1993", day: "12", category: "a", name: "Ben" },
{ birthdate: "1993", day: "13", category: "a", name: "John" },
{ birthdate: "1993", day: "14", category: "b", name: "Larry" },
{ birthdate: "1994", day: "15", category: "", name: "Nicole" },
];
// Top level: group by birthdate
const dataByBirthdate = _.groupBy(data, d => d.birthdate);
// Convert object into array of objects
const transformedData = Object.entries(dataByBirthdate).map(entry => {
const [birthdate, value] = entry;
// Second level: group by category
const birthdatedetailByCategory = _.groupBy(value, d => d.category);
const birthdatedetail = Object.entries(birthdatedetailByCategory).map(entry => {
const [category, value] = entry;
// Step through all entries to ensure we remove keys of your selection
const categorydetail = value.map(d => {
delete d.birthdate;
delete d.category;
return d;
});
return { category, categorydetail };
});
return { birthdate, birthdatedetail };
});
console.log(transformedData);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
This is function to group array with lodash:
const formatedResponse = _(arr)
.groupBy("birthdate")
.map((items, birthdate) => {
const birthdateDetails = _(items)
.groupBy("category")
.map((items, category) => {
return {
category,
categoryDetails: items.map(({ day, name }) => ({ day, name }))
};
})
.value();
return {
birthdate,
birthdateDetails
};
})
.value();
Maybe something like this
const data = [
{ birthdate: "1993", day: "12", category: "a", name: "Ben" },
{ birthdate: "1993", day: "13", category: "a", name: "John" },
{ birthdate: "1993", day: "14", category: "b", name: "Larry" },
{ birthdate: "1994", day: "15", category: "", name: "Nicole" },
];
const group = (data) => {
return _(data)
.groupBy('birthdate')
.map((birthdateItems, birthdate) => ({
birthdate,
birthdateDetail: _(birthdateItems)
.groupBy('category')
.map((catgoryItems, category) => ({
category,
categoryDetail: _(catgoryItems)
.map(item => _.omit(item, ['birthdate', 'category']))
.value()
}))
.value()
}))
.value();
}
console.log(group(data));
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.21/lodash.min.js"></script>
I need to convert an array with only ids and an object with Id & Name need to find object array element from the object and create new Object
App.js:
["111","114","117']
Object:
[
{ id: "111", Name: "Jerry" },
{ id: "112", Name: "Tom" },
{ id: "113", Name: "Mouse" },
{ id: "114", Name: "Minny" },
{ id: "115", Name: "Mayavi" },
{ id: "116", Name: "Kuttoosan" },
{ id: "117", Name: "Raju" }
];
Result Need:
[
{ id: "111", Name: "Jerry" },
{ id: "114", Name: "Minny" },
{ id: "117", Name: "Raju" }
];
const array = ["111", "114", "117"];
const object = [
{ id: "111", Name: "Jerry" },
{ id: "112", Name: "Tom" },
{ id: "113", Name: "Mouse" },
{ id: "114", Name: "Minny" },
{ id: "115", Name: "Mayavi" },
{ id: "116", Name: "Kuttoosan" },
{ id: "117", Name: "Raju" }
];
const result = object.filter(o => array.includes(o.id));
This should give you the result you want, pay attention that what you called object actually is an array of objects, as far as i understood you want keep only the object with an id contained in the first array, so as i shown just filter them
I think you can just use .filter() to achieve the same result.
const targetIds = ["111","114","117"];
const nameObjects = [{id:"111", Name:"Jerry"}, {id:"112", Name:"Tom"}, {id:"113", Name:"Mouse"}, {id:"114", Name:"Minny"}, {id:"115", Name:"Mayavi"}, {id:"116", Name:"Kuttoosan"}, {id:"117", Name:"Raju"}];
const filtered = nameObjects.filter((obj) => targetIds.indexOf(obj.id) !== -1);
// Which should give the result you need
// [{id:"111", Name:"Jerry"}, {id:"114", Name:"Minny"},{id:"117", Name:"Raju"}]
Use the .filter() to get the result
const ids = ["111", "114", "117"];
const nameids = [
{ id: "111", Name: "Jerry" },
{ id: "112", Name: "Tom" },
{ id: "113", Name: "Mouse" },
{ id: "114", Name: "Minny" },
{ id: "115", Name: "Mayavi" },
{ id: "116", Name: "Kuttoosan" },
{ id: "117", Name: "Raju" }
];
const result = nameids.filter(res => ids.includes(res.id));
console.log(result);
I have two arrays of objects as shown below :
categories = [
{ name: "performance", id: 1 },
{ name: "understanding", id: 2 }
]
queries = [
{ name: "A", categoryId: "1" },
{ name: "B", categoryId: "1" },
{ name: "C", categoryId: "1" },
{ name: "D", categoryId: "2" }
]
Now, using these two arrays of objects, I need following array as a result:
process = [
{ category: "performance", query: [
{ name: "A" },
{ name: "B" },
{ name: "C" }
]},
{ category: "understanding", query: [{ name: "D" }] }
]
I have to match the categoryId with process's id and then create the above array.
I have tried the following way to solve this but could not get desired result.
const test = [];
categories.forEach((element) => {
const r = que.filter((res) => res.categoryId === element.id);
queries.forEach((rt) => {
if (rt.categoryId === element.id) {
test.push({
category: element.name,
query: [{
name: rt.name,
}],
});
}
});
});
Is this possible using any built in array methods in JavaScript?
Thanks in advance
Using Array.reduce, you can group queries by categoryId.
Based on that groupedBy object, using Array.map, you can get the result you want.
const categories = [{
name: "performance",
id: "1"
},{
name: "understanding",
id: "2"
}];
const queries = [{
name: "A",
categoryId: "1"
}, {
name: "B",
categoryId: "1"
}, {
name: "C",
categoryId: "1"
}, {
name: "D",
categoryId: "2"
}];
const groupByQueries = queries.reduce((acc, cur) => {
acc[cur.categoryId] ?
acc[cur.categoryId].push({ name: cur.name })
: acc[cur.categoryId] = [ { name: cur.name } ];
return acc;
}, {});
const result = categories.map(({ name, id }) => ({
category: name,
query: groupByQueries[id]
}));
console.log(result);
categories = [{name: "performance", id: 1},{name: "understanding", id: 2}];
queries = [{name: "A", categoryId: "1"}, {name: "B", categoryId: "1"}, {name: "C", categoryId: "1"}, {name: "D", categoryId: "2"}]
const test = [];
categories.forEach((element) => {
let temp = []
queries.map(item =>{
if(element.id.toString() === item.categoryId)
temp.push({name: item.name})
})
test.push({category:element.name,query:temp})
});
console.log(test)
I have two array of objects.
const firstObj = [
{ Id: "1", Name: "Peter" },
{ Id: "2", Name: "John" },
{ Id: "12", Name: "jessy" },
];
const secondObj = [
{ Id: "1", Name: "Roa", original: { Id: "1" } },
{ Id: "2", Name: "John2", original: { Id: "2" } },
{ Id: "5", Name: "Rachel", original: { Id: "3" } },
];
Here, I am trying to filter data on the basis of Id and return the filtered firstObj
So, here firstObj has an entry { Id: "12", Name: "jessy" } this object whose Id does not match with the secondObj.original.Id so, firstObj will have the given result.
what I tried was
firstObj.filter(
firstObj,
secondObj.map((second) => {
return firstObj.Id === second.original.Id;
}),
);
But this does not work. Can any one help me out here , using Lodash or Js filter.
output would be -> [{"Id": "3", "Name": "jessy"}]
Is this what you are looking for?
const firstObj = [
{ Id: "1", Name: "Peter" },
{ Id: "2", Name: "John" },
{ Id: "12", Name: "jessy" },
];
const secondObj = [
{ Id: "1", Name: "Roa", original: { Id: "1" } },
{ Id: "2", Name: "John2", original: { Id: "2" } },
{ Id: "1", Name: "Rachel", original: { Id: "3" } },
];
const result = lodash.filter(firstObj, (firstItem) => {
return !lodash.some(secondObj, (secondItem) => {
return firstItem.Id === secondItem.original.Id;
});
});
Live: https://stackblitz.com/edit/js-addy5t?file=index.js
why use loadash for this. you can use native filter and find functions to achieve this
const filter = firstObj.filter(item => {
return secondObj.find(itm => itm.original.id === item.id)
});
You could use native method of Array.filter() and Array.some() to achieve the desired output.
Please check below working code snippet
ES6
const firstObj = [
{ Id: "1", Name: "Peter" },
{ Id: "2", Name: "John" },
{ Id: "12", Name: "jessy" },
],
secondObj = [
{ Id: "1", Name: "Roa", original: { Id: "1" } },
{ Id: "2", Name: "John2", original: { Id: "2" } },
{ Id: "1", Name: "Rachel", original: { Id: "3" } },
];
let result = firstObj.filter(({Id})=> !secondObj.some(item => item.original.Id === Id));
console.log(result)
I nave an array:
const arr = [
{ name: "aa", type: "total", count: 28394 },
{ name: "aa", type: "featured", count: 4 },
{ name: "aa", type: "noAnswers", count: 5816 },
{ name: "ba", type: "total", count: 148902 },
{ name: "ba", type: "featured", count: 13 },
{ name: "ba", type: "noAnswers", count: 32527 },
{ name: "cc", type: "total", count: 120531 },
{ name: "cc", type: "featured", count: 6 },
{ name: "cc", type: "noAnswers", count: 24170 }
];
const arrResult = [
{ name: "aa", total: 28394, featured: 4, noAnswers: 5816 },
{ name: "ba", total: 148902, featured: 13, noAnswers: 32527 },
{ name: "cc", total: 120531, featured: 6, noAnswers: 24170 }
];
I come up with this code:
let output = [];
const unique = [...new Set(arr.map(item => item.name))];
for(const key of unique) {
let result = arr.filter(x => {
return x.name === key;
});
output.push({
name: key,
// need to get the rest of the properties here
// total
// featured
// noAnswers
});
}
The only one thing I can not figure out is how to get the property names.
Any ideas?
You can try something like this:
Idea:
Create a hashMap so you can group objects via name.
Then, add necessary properties to this group.
Finally, loop over keys and create final object with name property added back.
const arr = [ { name: "aa", type: "total", count: 28394 }, { name: "aa", type: "featured", count: 4 }, { name: "aa", type: "noAnswers", count: 5816 }, { name: "ba", type: "total", count: 148902 }, { name: "ba", type: "featured", count: 13 }, { name: "ba", type: "noAnswers", count: 32527 }, { name: "cc", type: "total", count: 120531 }, { name: "cc", type: "featured", count: 6 }, { name: "cc", type: "noAnswers", count: 24170 } ];
const hashMap = arr.reduce((acc, item) => {
acc[item.name] = acc[item.name] || {};
acc[item.name][item.type] = item.count;
return acc;
}, {});
const result = Object.keys(hashMap).map((name) => Object.assign({}, {name}, hashMap[name] ));
console.log(result)
Working:
What I'm doing is I'm creating a new object for every new name. So, this: acc[item.name] = acc[item.name] || {}; checks if the entry is unavailable or not.
If unavailable, return a new object.
If available, return same object's reference.
So for any given name, you will only refer to same object.
Now this: acc[item.name][item.type] = item.count sets the properties. As we are referring to same object, you are setting property in one place. So if you have duplicate entries, say
[
{ name: "aa", type: "total", count: 28394 },
{ name: "aa", type: "total", count: 123},
]
output will have total: 123 instead.
So, at the end, you have a structure like:
{
aa: {
total: <something>,
feature: <something>,
...
}
}
Now all you have to do is merge the name in this object and return the value. You can also create the object with name property as default (as done by adiga). Thats something I didn't think while answering. So crediting instead of answering.
You can use reduce and destructuring like this:
The idea is to create an object with key as the name property and value as the final objects you need in the output. So, that you can simply use Object.values to get the final array:
const arr=[{name:"aa",type:"total",count:28394},{name:"aa",type:"featured",count:4},{name:"aa",type:"noAnswers",count:5816},{name:"ba",type:"total",count:148902},{name:"ba",type:"featured",count:13},{name:"ba",type:"noAnswers",count:32527},{name:"cc",type:"total",count:120531},{name:"cc",type:"featured",count:6},{name:"cc",type:"noAnswers",count:24170}];
const merged = arr.reduce((acc,{name,type,count}) =>
((acc[name] = acc[name] || {name})[type] = count, acc)
,{})
console.log(Object.values(merged))
This is equivalent to :
const arr=[{name:"aa",type:"total",count:28394},{name:"aa",type:"featured",count:4},{name:"aa",type:"noAnswers",count:5816},{name:"ba",type:"total",count:148902},{name:"ba",type:"featured",count:13},{name:"ba",type:"noAnswers",count:32527},{name:"cc",type:"total",count:120531},{name:"cc",type:"featured",count:6},{name:"cc",type:"noAnswers",count:24170}];
/* Our goal is to create a merged object like this:
{
"aa": {
"name": "aa",
"total": 28394,
"featured": 4,
"noAnswers": 5816
},
"ba": {
"name": "ba",
"total": 148902,
....
},
"cc": {
"name": "cc",
......
}
}
The advantage of using object accumulator is we can access it like this: acc[name]
*/
const merged = arr.reduce((acc, {name,type,count} /*Destructuring*/) => {
/* if the accumulator doesn't have the current "name" key,
create new object
else use the existing one;
{name} is same as {name: name}
*/
acc[name] = acc[name] || {name};
/* To the inner object,
add a key with the "type" value and assign it to "count" value
*/
acc[name][type] = count;
// return the accumulator
return acc;
}, {})
// use Object.values to get the value part of the merged obejct into an array
console.log(Object.values(merged))
var op = {name : key};
for(i=0; i < result.length; i++){
op[result[i].type] = result[i].count;
}
output.push(op);
just adding this will work fine. However your code is not the most efficient.
Hashing based on name will make it faster
const arr = [
{ name: "aa", type: "total", count: 28394 },
{ name: "aa", type: "featured", count: 4 },
{ name: "aa", type: "noAnswers", count: 5816 },
{ name: "ba", type: "total", count: 148902 },
{ name: "ba", type: "featured", count: 13 },
{ name: "ba", type: "noAnswers", count: 32527 },
{ name: "cc", type: "total", count: 120531 },
{ name: "cc", type: "featured", count: 6 },
{ name: "cc", type: "noAnswers", count: 24170 }
];
let output = [];
const unique = [...new Set(arr.map(item => item.name))];
for(const key of unique) {
let result = arr.filter(x => {
return x.name === key;
});
var op = {name : key};
for(i=0; i < result.length; i++){
op[result[i].type] = result[i].count;
}
output.push(op);
}
console.log(output);
The following is the most efficient way of doing it :
const arr = [
{ name: "aa", type: "total", count: 28394 },
{ name: "aa", type: "featured", count: 4 },
{ name: "aa", type: "noAnswers", count: 5816 },
{ name: "ba", type: "total", count: 148902 },
{ name: "ba", type: "featured", count: 13 },
{ name: "ba", type: "noAnswers", count: 32527 },
{ name: "cc", type: "total", count: 120531 },
{ name: "cc", type: "featured", count: 6 },
{ name: "cc", type: "noAnswers", count: 24170 }
];
var hash = {};
var result = [];
for(var i=0; i < arr.length; i++){
if(!arr[i].name in hash)
hash[arr[i].name] = {}
let temp = {};
temp[arr[i].type] = arr[i].count;
hash[arr[i].name] = Object.assign(temp, hash[arr[i].name]);
}
for(var key in hash)
result.push({name : key, ...hash[key]})
console.log(result)
You can use find operator of javascript to grab the desired row from arrResult Change your code like below-
for(const key of unique) {
let result = arr.filter(x => {
return x.name === key;
});
var currResult = arrResult.find(x => x.name == key);
output.push({
name: key,
// need to get the rest of the properties here
total: currResult.total,
featured: currResult.featured,
noAnswers: currResult.noAnswers
});
}
JSFiddle: https://jsfiddle.net/ashhaq12345/z8royg5w/
const arr = [
{ name: "aa", type: "total", count: 28394 },
{ name: "aa", type: "featured", count: 4 },
{ name: "aa", type: "noAnswers", count: 5816 },
{ name: "ba", type: "total", count: 148902 },
{ name: "ba", type: "featured", count: 13 },
{ name: "ba", type: "noAnswers", count: 32527 },
{ name: "cc", type: "total", count: 120531 },
{ name: "cc", type: "featured", count: 6 },
{ name: "cc", type: "noAnswers", count: 24170 }
];
const names = [...new Set(arr.map(item => item.name))]
const output = {};
names.forEach(name => {output[name] = {}});
arr.forEach(item => {
output[item.name][item.type] = item.count
});
const result = Object.entries(output).map(([name, rest]) => ({name, ...rest}))
console.log(result);
const arrResult = [
{ name: "aa", total: 28394, featured: 4, noAnswers: 5816 },
{ name: "ba", total: 148902, featured: 13, noAnswers: 32527 },
{ name: "cc", total: 120531, featured: 6, noAnswers: 24170 }
];
You can simply use for loop to iterate over your array and take a temp array and take map and fill the map using required data and then push your map into temp array like following.
const arr = [
{ name: "aa", type: "total", count: 28394 },
{ name: "aa", type: "featured", count: 4 },
{ name: "aa", type: "noAnswers", count: 5816 },
{ name: "ba", type: "total", count: 148902 },
{ name: "ba", type: "featured", count: 13 },
{ name: "ba", type: "noAnswers", count: 32527 },
{ name: "cc", type: "total", count: 120531 },
{ name: "cc", type: "featured", count: 6 },
{ name: "cc", type: "noAnswers", count: 24170 }
];
let result = [];
for( var i = 0; i < arr.length; i++)
{
let data = {};
if( arr[i].type == 'total')
{
data.name = arr[i].name;
data.total = arr[i].count;
data.featured = arr[i+1].count;
data.noAnswers = arr[i+2].count;
result.push(data);
}
}
console.log(result);