Multi level grouping and populate arrays with using lodash - javascript

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>

Related

React Hooks: How to update a nested array property in a state?

I have a state of arrays. The array has an array nested inside it. This is what my state looks like:
const [record, setRecord] = useState([
{
name: "Pink Floyd",
rank: 1,
songs: [
{
name: "A",
year: "1970",
downloads: "10",
},
{
name: "B",
year: "1980",
downloads: "20",
},
{
name: "C",
year: "1990",
downloads: "5",
},
],
},
{
name: "Led Zeppelin",
rank: 2,
songs: [
{
name: "D",
year: "1965",
downloads: "25",
},
{
name: "E",
year: "1975",
downloads: "65",
},
{
name: "F",
year: "1985",
downloads: "90",
},
],
},
]);
I am able to update the rank property by doing this :
setRecord(prevRecord =>
prevRecord.map((el) =>
el.name == "someName" ? { ...el, rank: "someRank" } : el
)
);
Now, I want to update the 'downloads' property which is nested inside songs. How can I update the downloads property?
you must iterate through your state and look for item you want to change
const newState = record.map((item) => {
if (item.name === 'YOUR_DESIRE_NAME_TO_MODIFY') {
const newSongs = item.songs.map((song) => {
if (song.name === 'DESIRE_SONG_NAME_TO_MODIFY') {
song.downloads = '100'
}
return song
})
item.songs = newSongs
}
return item
})
setRecord(newState)

Creating an array from 2 other arrays

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)

use filter of Lodash/Javascript to get the filtered data

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)

How to convert a JavaScript array to an object with a specified structure?

I have an array like the example logicData below.
I want to pass this array to a function that returns an object like the returnObject below.
In addition, using the returned object, I need to re-create the original logicData array.
Please anyone help me out. Thanks in Advance.
// array
var logicData = [
{
details: {
name: "User-1",
id: 1,
age: "38",
},
company: "XYX",
position: "Accountant",
},
{
details: {
name: "User-2",
id: 2,
age: "55",
},
company: "XYX",
position: "Sales executive",
},
{
details: {
name: "User-3",
id: 3,
age: "25",
},
company: "XYX",
position: "Engineer",
},
{
details: {
name: "User-4",
id: 4,
age: "29",
},
company: "XYX",
position: "Engineer",
},
{
details: {
name: "User-5",
id: 5,
age: "32",
},
company: "XYX",
position: "Sales executive",
}
]
How to convert the array to below format
// require object formate
const returnObject = {
first: {
details: {
name: "User-1",
id: "1",
age: "38",
},
company: "XYX",
position: "Accountant",
},
second: {
first: {
details: {
name: "User-2",
id: 2,
age: "55",
},
company: "XYX",
position: "Sales executive",
},
second: {
first: {
details: {
name: "User-3",
id: 3,
age: "25",
},
company: "XYX",
position: "Engineer",
},
second: {
first: {
details: {
name: "User-4",
id: 4,
age: "29",
},
company: "XYX",
position: "Engineer",
},
second: {
details: {
name: "User-5",
id: 5,
age: "32",
},
company: "XYX",
position: "Sales executive",
}
}
}
}
}
for converting structures like logicData to returnObject, you can use/reuse the following function:
function convertArrToObj(arr){
let res = arr[arr.length - 1];
for(let i = arr.length - 1; i > 0; i--){
res = {
first: arr[i - 1],
second: res,
}
}
return res;
}
convertArrToObj(logicData);
and for reverting the structures like returnObject back to logicData array structure, you can use/reuse this function:
function revertObjToArr(obj){
const res = [];
let temp = obj;
while("second" in temp){
res.push(temp.first);
temp = temp.second;
}
res.push(temp);
return res;
}
revertObjToArr(returnObject);

JavaScript array map while keeping original elements in the array

I have this array.
const data = [
{ name: "A", age: "12" },
{ name: "B", age: "5" },
{ name: "C", age: "6" }
];
I want to add additional property called key to this array like this.
const data = [
{ name: "A", age: "12", key: "A12" },
{ name: "B", age: "5", key: "B5" },
{ name: "C", age: "6", key: "C6" }
];
I tired this map function and output is not the one I expect. How do I achieve this using JS map function..
const data = [
{ name: "A", age: "12"},
{ name: "B", age: "5"},
{ name: "C", age: "6"}
];
console.log(
"MAP",
data.map(element => (element.key = element.name + element.age))
);
You need to return an object from the map callback:
const data = [
{ name: "A", age: "12"},
{ name: "B", age: "5"},
{ name: "C", age: "6"}
];
console.log(
data.map(({ name, age }) => ({ name, age, key: name + age }))
);
If you wish to mutate the existing objects, then use forEach instead of .map:
const data = [
{ name: "A", age: "12"},
{ name: "B", age: "5"},
{ name: "C", age: "6"}
];
data.forEach((obj) => {
obj.key = obj.name + obj.age;
});
console.log(data);
Return element as well using the comma operator:
const data = [
{ name: "A", age: "12"},
{ name: "B", age: "5"},
{ name: "C", age: "6"}
];
console.log(data.map(element => (element.key = element.name + element.age, element)));
.as-console-wrapper { max-height: 100% !important; top: auto; }
Or just change your map callback to make it simpler with destructuring:
const data = [
{ name: "A", age: "12"},
{ name: "B", age: "5"},
{ name: "C", age: "6"}
];
console.log(data.map(({ name, age }) => ({ name, age, key: name + age})));
.as-console-wrapper { max-height: 100% !important; top: auto; }
There are more than one way to do this and the Array.map is by far the most concise and clean way to do it as already provided. Here are few other methods:
const data = [ { name: "A", age: "12" }, { name: "B", age: "5" }, { name: "C", age: "6" } ];
let from = Array.from(data, ({name, age}) => ({name, age, key: name+age}))
let assign = data.map(({name, age}) => Object.assign({name, age}, {key: name+age}))
console.log('from: ', from)
console.log('assign: ', assign)
If you want to generate a key from all values (assuming all are primitives) you can also do this:
const data = [
{ name: "A", age: "12", city: 'Miami' }, // "key": "A12Miami"
{ name: "B", age: "2", gender: 'M'}, // "key": "B2M"
];
let all = data.map(obj =>
({...obj, ...Object.fromEntries([['key', Object.values(obj).join('')]])}))
console.log(all)
This way you do not have to specify or care about the pop names etc.

Categories