Related
I need to calculate a score based on departments in groups. To simplify that I used only 1 group as an example.
I'm receiving this data structure:
const data = [{
"id": "cklt7ln1k0922o0sabjkk74m9",
"score": 80,
"count": 1,
"department": "Engineering",
"group": "Group one"
},
{
"id": "cklt7ln1k0922o0sabjkk74m9",
"score": 40,
"count": 1,
"department": "Executive",
"group": "Group one"
},
{
"id": "cklt7ln1k0922o0sabjkk74m9",
"score": 40,
"count": 1,
"department": "OOO Boost",
"group": "Group one"
},
{
"id": "cklt7ln1k0922o0sabjkk74m9",
"score": 60,
"count": 1,
"department": "Engineering",
"group": "Group one"
},
{
"id": "cklt7ln1k0922o0sabjkk74m9",
"score": 100,
"count": 2,
"department": "Supporting Department",
"group": "Group one"
},
{
"id": "cklt7ln1k0922o0sabjkk74m9",
"score": 20,
"count": 1,
"department": "Designers",
"group": "Group one"
},
{
"id": "cklt7ln1k0922o0sabjkk74m9",
"score": 20,
"count": 1,
"department": "Executive",
"group": "Group one"
}
];
and I need to create a data structure for a heatmap:
const heatmapData = [
{
row: 'Group one',
columns: [
{
name: 'Engineering',
averageScore: 70, // (80[score] * 1[count] + 60[score] * 1[count]) / 2[sum of count -> 1+1]
},
{
name: 'Supporting Department',
averageScore: 100, // 100[score] * 2[count] / 2[sum of count -> 2]
},
.... next unique departments
]
}
]
I'm a bit stacked with some simple solution of grouping data with calculation. Could you help me please? Thank you
You can group your data based on the group and for each group, group it on department and sum up score and count. Once you have this object, you can calculate average for each department.
const data = [{ "id": "cklt7ln1k0922o0sabjkk74m9", "score": 80, "count": 1, "department": "Engineering", "group": "Group one" }, { "id": "cklt7ln1k0922o0sabjkk74m9", "score": 40, "count": 1, "department": "Executive", "group": "Group one" }, { "id": "cklt7ln1k0922o0sabjkk74m9", "score": 40, "count": 1, "department": "OOO Boost", "group": "Group one" }, { "id": "cklt7ln1k0922o0sabjkk74m9", "score": 60, "count": 1, "department": "Engineering", "group": "Group one" }, { "id": "cklt7ln1k0922o0sabjkk74m9", "score": 100, "count": 2, "department": "Supporting Department", "group": "Group one" }, { "id": "cklt7ln1k0922o0sabjkk74m9", "score": 20, "count": 1, "department": "Designers", "group": "Group one" }, { "id": "cklt7ln1k0922o0sabjkk74m9", "score": 20, "count": 1, "department": "Executive", "group": "Group one" } ],
grouped = data.reduce((r, {group, department, score, count}) => {
r[group] = r[group] || {};
r[group][department] = r[group][department] || {department, totalScore: 0, count: 0 };
r[group][department].totalScore += score * count;
r[group][department].count += count;
return r;
},{}),
result = Object.keys(grouped).map(k => {
const columns = Object.values(grouped[k]).map(d => ({
department: d.department,
averageScore: d.totalScore/d.count
}));
return {row: k, columns };
})
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can group items into the dictionary:
let group = data.reduce((acc, val) => {
if(!acc[val.group]) acc[val.group] = {};
if(!acc[val.group][val.department]) acc[val.group][val.department] = {sum:0, count: 0, averageScore: 0};
acc[val.group][val.department] = {
sum: acc[val.group][val.department].sum+val.score,
count: acc[val.group][val.department].count + 1,
averageScore: (acc[val.group][val.department].sum+val.score) / (acc[val.group][val.department].count + 1)
}
return acc
}, {});
then map it to requested format:
Object.keys(group).map(row => {
return {
row: row,
columns: Object.keys(group[row]).map(col => {return {name: col, averageScore: group[row][col].averageScore}})
}
})
Allow me to explain how it works.. so we know that objects are pointers more than they are values.. I used that principle throughout the solution. I have the array to return(where I push stuff), then I had 2 Objects to store values(and editing those Objects edited the "values" in the array), so in the array you pass in, I basically add values to these objects and link them in the array(the format you desire)
UPDATED AGAIN.. I applied the logic of your calculation examples >:D
function group(array){
let arr=[]; let groups={}; let departments={}
array.forEach(a=>{
if(!groups[a.group]){ //setting each group
groups[a.group]={row:a.group,columns:[]}
arr.push(groups[a.group])
}
if(!departments[a.department]){ //setting each department
departments[a.department]=[{name:a.department,averageScore:a.score*a.count},a.count] //score * count and count(because of your calculation examples)
groups[a.group].columns.push(departments[a.department][0])
}
else{ //adding department when they already exist
departments[a.department][1]+=a.count //count
departments[a.department][0].averageScore+=a.score*a.count //score * count
//(because of your calculation examples)
}
})
Object.keys(departments).forEach(a=>departments[a][0].averageScore/=departments[a][1])
return arr
}
console.log(group(data))
<script>
//to not take out unnecesary space in the answer
window.data = [{
"id": "cklt7ln1k0922o0sabjkk74m9",
"score": 80,
"count": 1,
"department": "Engineering",
"group": "Group one"
},
{
"id": "cklt7ln1k0922o0sabjkk74m9",
"score": 40,
"count": 1,
"department": "Executive",
"group": "Group one"
},
{
"id": "cklt7ln1k0922o0sabjkk74m9",
"score": 40,
"count": 1,
"department": "OOO Boost",
"group": "Group one"
},
{
"id": "cklt7ln1k0922o0sabjkk74m9",
"score": 60,
"count": 1,
"department": "Engineering",
"group": "Group one"
},
{
"id": "cklt7ln1k0922o0sabjkk74m9",
"score": 100,
"count": 2,
"department": "Supporting Department",
"group": "Group one"
},
{
"id": "cklt7ln1k0922o0sabjkk74m9",
"score": 20,
"count": 1,
"department": "Designers",
"group": "Group one"
},
{
"id": "cklt7ln1k0922o0sabjkk74m9",
"score": 20,
"count": 1,
"department": "Executive",
"group": "Group one"
}
];
</script>
I have a json file which contains the list of products.
[{"id":76,
"name":"A",
"description":"abc",
"price":199,
"imageUrl":"image.jpg",
"productCategory":[{
"categoryId":5,
"category":null
},{
"categoryId":6,
"category":null
}
]}
I then have a second json file with a list of categories which look like so:
[{"id":5,"name":"red"},
{"id":6,"name”:"blue"}]
What is the best way to join the categories of this two json files in Angular?
This is what I aim to achieve:
[{"id":76,
"name":"A",
"description":"abc",
"price":199,
"imageUrl":"image.jpg",
"productCategory":[{
"categoryId":5,
"category":red
},{
"categoryId":6,
"category":blue
}
]}
You can use filter function for your requirement as below
let products = [{
"id": 76,
"name": "A",
"description": "abc",
"price": 199,
"imageUrl": "image.jpg",
"productCategory": [{
"categoryId": 2,
"category": null
}, {
"categoryId": 1,
"category": null
}]
}, {
"id": 77,
"name": "B",
"description": "abcd",
"price": 1997,
"imageUrl": "image.jpg",
"productCategory": [{
"categoryId": 5,
"category": null
}, {
"categoryId": 6,
"category": null
}]
},
{
"id": 78,
"name": "C",
"description": "abcde",
"price": 1993,
"imageUrl": "image.jpg",
"productCategory": [{
"categoryId": 4,
"category": null
}, {
"categoryId": 6,
"category": null
}]
}];
let category = [{ "id": 5, "name": "red" }, { "id": 6, "name": "blue" }]
let result = products.filter(p => {
var exist = p.productCategory.filter(pc => category.find(c => c.id == pc.categoryId))[0];
return exist;
});
console.log(result);
let products = [{
"id": 76,
"name": "A",
"description": "abc",
"price": 199,
"imageUrl": "image.jpg",
"productCategory": [{
"categoryId": 2,
"category": null
}, {
"categoryId": 1,
"category": null
}]
}, {
"id": 77,
"name": "B",
"description": "abcd",
"price": 1997,
"imageUrl": "image.jpg",
"productCategory": [{
"categoryId": 5,
"category": null
}, {
"categoryId": 6,
"category": null
}]
},
{
"id": 78,
"name": "C",
"description": "abcde",
"price": 1993,
"imageUrl": "image.jpg",
"productCategory": [{
"categoryId": 4,
"category": null
}, {
"categoryId": 6,
"category": null
}]
}];
let category = [{ "id": 5, "name": "red" }, { "id": 6, "name": "blue" }]
let result = products.filter(p => {
var exist = p.productCategory.filter(pc => category.find(c => c.id == pc.categoryId))[0];
return exist;
});
console.log(result);
I make a stackblitz that use a service to retreive the data. Yes, the way is using switchMap and map. SwitchMap receive an array and must return an observable. with map, we transform the data received and return the data transformed
this.dataService.getCategories().pipe(
//first get the categories, the categories is in the
//variable cats
switchMap((cats:any[])=>{
return this.dataService.getProducts().pipe(map((res:any[])=>{
res.forEach(p=>{ //with each product
p.productCategory.forEach(c=>{ //with each productCategory in product
//equals a propertie "category" to the propertie "name" of the cats
c.category=cats.find(x=>x.id==c.categoryId).name
})
})
return res
}))
})).subscribe(res=>{
console.log(res)
})
If only has an unique product we can make
this.dataService.getCategories().pipe(
switchMap((cats:any[])=>{
return this.dataService.getUniqProduct(2).pipe(map((res:any)=>{
res.productCategory.forEach(c=>{
c.category=cats.find(x=>x.id==c.categoryId).name
})
return res
}))
})).subscribe(res=>{
console.log(res)
})
We can improve our dataService "cached" the categories
getCategories() {
if (this.categories)
return of(this.categories);
return http.get(......).pipe(tap(res=>{
this.categories=res;
}))
}
NOTE:In the stackbit I simulate the call to an http.get(...) using "of"
I've been trying to transform some data using lodash without success. I am really new to javascript and lodash. How can I get the expected result?
I've used mapValues and chain, but I didn't achieve anything good.
const data = {
"north": [
{
"2018-07-01": {
"date": "2018-07-01",
"name": "david",
"age": 11
},
"2018-07-02": {
"date": "2018-07-02",
"name": "damo",
"age": 16
},
"2018-07-03": {
"date": "2018-07-03",
"name": "dani",
"age": 12
}
}
],
"south": [
{
"2018-07-01": [
{
"fruit": "banana",
"date": "2018-07-01",
"name": "miller",
"age": 11
},
{
"fruit": "mango",
"date": "2018-07-01",
"name": "mano",
"age": 11
},
{
"fruit": "avocado",
"date": "2018-07-01",
"name": "karl",
"age": 14
}
],
"2018-07-02": [
{
"fruit": "pineaplle",
"date": "2018-07-02",
"name": "gautier",
"age": 12
},
{
"fruit": "apple",
"date": "2018-07-02",
"name": "gauteng",
"age": 9
},
{
"fruit": "watermelon",
"date": "2018-07-02",
"name": "garzier",
"age": 12
}
]
}
]
};
Below is the expected result. I am trying to remove the dates which are outside the objects and arrays.
const expectedData = {
"north": [
{
"date": "2018-07-01",
"name": "david",
"age": 11
},
{
"date": "2018-07-02",
"name": "damo",
"age": 16
},
{
"date": "2018-07-03",
"name": "dani",
"age": 12
}
],
"south": [
{
"fruit": "banana",
"date": "2018-07-01",
"name": "miller",
"age": 11
},
{
"fruit": "mango",
"date": "2018-07-01",
"name": "mano",
"age": 11
},
{
"fruit": "avocado",
"date": "2018-07-01",
"name": "karl",
"age": 14
},
{
"fruit": "pineaplle",
"date": "2018-07-02",
"name": "gautier",
"age": 12
},
{
"fruit": "apple",
"date": "2018-07-02",
"name": "gauteng",
"age": 9
},
{
"fruit": "watermelon",
"date": "2018-07-02",
"name": "garzier",
"age": 12
}
]
};
You don't really need lodash for this. You can look at each key in your data and the just pull the values from each element of the array and concat it into a new array.
const data = {"north": [{"2018-07-01": {"date": "2018-07-01","name": "david","age": 11},"2018-07-02": {"date": "2018-07-02","name": "damo","age": 16},"2018-07-03": {"date": "2018-07-03","name": "dani","age": 12}}],"south": [{"2018-07-01": [{"fruit": "banana","date": "2018-07-01","name": "miller","age": 11},{"fruit": "mango","date": "2018-07-01","name": "mano","age": 11},{"fruit": "avocado","date": "2018-07-01","name": "karl","age": 14}],"2018-07-02": [{"fruit": "pineaplle","date": "2018-07-02","name": "gautier","age": 12},{"fruit": "apple","date": "2018-07-02","name": "gauteng","age": 9},{"fruit": "watermelon","date": "2018-07-02","name": "garzier","age": 12}]}]};
Object.keys(data).forEach(k => {
data[k] = data[k].reduce((a, c) => a.concat(...Object.values(c)), [])
})
console.log(data)
This starts with each key in your original object north and south. And for each one replaces the array with the accumulated values of each object in that array ignoring the keys.
alternatively you could just do this
const expectedData = {
north: Object.values(data.north[0]),
south: Object.values(data.south[0])
}
You can do any lodash quick operations, in pure JS way. But, since you tagged lodash
here is the version:
_.mapValues(data, v => _.flatMapDeep(v, _.values))
var data = {
"north": [
{
"2018-07-01": {
"date": "2018-07-01",
"name": "david",
"age": 11
},
"2018-07-02": {
"date": "2018-07-02",
"name": "damo",
"age": 16
},
"2018-07-03": {
"date": "2018-07-03",
"name": "dani",
"age": 12
}
}
],
"south": [
{
"2018-07-01": [
{
"fruit": "banana",
"date": "2018-07-01",
"name": "miller",
"age": 11
},
{
"fruit": "mango",
"date": "2018-07-01",
"name": "mano",
"age": 11
},
{
"fruit": "avocado",
"date": "2018-07-01",
"name": "karl",
"age": 14
}
],
"2018-07-02": [
{
"fruit": "pineaplle",
"date": "2018-07-02",
"name": "gautier",
"age": 12
},
{
"fruit": "apple",
"date": "2018-07-02",
"name": "gauteng",
"age": 9
},
{
"fruit": "watermelon",
"date": "2018-07-02",
"name": "garzier",
"age": 12
}
]
}
]
};
var expectedData = _.mapValues(data, v => _.flatMapDeep(v, _.values));
console.log(expectedData);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
Following code gets the result below in a way that multiple iterations required. I wonder what would be the way to make it happen in a single or less iterations. Thanks in advance.
var input = [{
"ActiveMembers": [{
"Id": 101,
"Name": "alpha"
}, {
"Id": 102,
"Name": "bravo"
}],
"Contents": [{
"Id": 2001,
"RowId": "517",
"Time": "19 Jan 2017",
"ViewCount": 1124
}, {
"Id": 2002,
"RowId": "518",
"Time": "Today, 07:02 PM",
"ViewCount": 62
}],
"TotalUsers": 3,
"UsersDetails": "2 members, 1 anonymous users"
}, {
"ActiveMembers": [{
"Id": 101,
"Name": "alpha"
}, {
"Id": 103,
"Name": "charlie"
}, {
"Id": 104,
"Name": "delta"
}, {
"Id": 105,
"Name": "bravo"
}],
"Contents": [{
"Id": 2002,
"RowId": "519",
"Time": "27 Jun 2017",
"ViewCount": 4833
}, {
"Id": 2041,
"RowId": "525",
"Time": "17 Feb 2015",
"ViewCount": 24491
}],
"TotalUsers": 23,
"UsersDetails": "4 members, 19 anonymous users"
}];
var contents = Array.prototype.concat.apply([], input.map(i => i.Contents));
var activeMembers = _.uniqBy(Array.prototype.concat.apply([], input.map(i => i.ActiveMembers)), (i) => i.Id);
var totalUsers = number = _.sumBy(input, (i) => i.TotalUsers);
var userDetails = string = input.map(i => i.UsersDetails).join(' ; ');
const result = new Object();
result.Contents = contents;
result.ActiveMembers = activeMembers;
result.TotalUsers = totalUsers;
result.UserDetails = userDetails;
console.log(JSON.stringify(result));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
Result
{
"ActiveMembers": [
{
"Id": 101,
"Name": "alpha"
},
{
"Id": 102,
"Name": "bravo"
},
{
"Id": 103,
"Name": "charlie"
},
{
"Id": 104,
"Name": "delta"
},
{
"Id": 105,
"Name": "bravo"
}
],
"Contents": [
{
"Id": 2001,
"RowId": "517",
"Time": "19 Jan 2017",
"ViewCount": 1124
},
{
"Id": 2002,
"RowId": "518",
"Time": "Today, 07:02 PM",
"ViewCount": 62
},
{
"Id": 2002,
"RowId": "519",
"Time": "27 Jun 2017",
"ViewCount": 4833
},
{
"Id": 2041,
"RowId": "525",
"Time": "17 Feb 2015",
"ViewCount": 24491
}
],
"TotalUsers": 26,
"UsersDetails": "2 members, 1 anonymous users;4 members, 19 anonymous users"
}
Aggregate the data in a single iteration.
let ActiveMembers = [];
let Contents = [];
let TotalUsers = 0;
let UserDetails = [];
input.forEach((item) => {
ActiveMembers = ActiveMembers.concat(item.ActiveMembers);
Contents = Contents.concat(item.Contents);
TotalUsers += item.TotalUsers;
UserDetails.push(item.UsersDetails);
});
const result = {
ActiveMembers: _.uniqBy(ActiveMembers, "Id"),
Contents: Contents,
TotalUsers: TotalUsers,
UserDetails: UserDetails.join(";")
};
console.log(JSON.stringify(result));
This question already has answers here:
How can I access and process nested objects, arrays, or JSON?
(31 answers)
Closed 6 years ago.
I have a JSON data with the following format:
activities
[
{
"id": 32,
"poi_id": 1,
"due_date": "2016-09-08T18:15:00.000Z",
"items": [
{
"id": 21,
"name": "Choluv jar : JAR",
"activity_id": 32
}
]
},
{
"id": 30,
"poi_id": 9,
"due_date": "2016-09-14T18:15:00.000Z",
"items": [
{
"id": 17,
"name": "Bourbon Family : PKT",
"activity_id": 30
},
{
"id": 18,
"name": "Choluv jar : JAR",
"activity_id": 30
}
]
},
{
"id": 29,
"poi_id": 1,
"due_date": "2016-09-27T18:15:00.000Z",
"items": [
{
"id": 16,
"name": "Choluv jar : JAR",
"activity_id": 29
}
]
}
]
I want to reformat this data using lodash or simply javascript to look like this:
/*poi_id is the key*/
"1": [{
"id": 32,
"poi_id": 1,
"due_date": "2016-09-08T18:15:00.000Z",
"items": {
/*due_date is the key*/
"2016-09-08T18:15:00.000Z": [{
"id": 21,
"name": "Choluv jar : JAR",
"activity_id": 32
}]
}
}, {
"id": 29,
"poi_id": 1,
"due_date": "2016-09-27T18:15:00.000Z",
"items": {
"2016-09-27T18:15:00.000Z": [{
"id": 16,
"name": "Choluv jar : JAR",
"activity_id": 29
}]
}
}],
"9": [{
"id": 30,
"poi_id": 9,
"due_date": "2016-09-14T18:15:00.000Z",
"items": {
"2016-09-14T18:15:00.000Z": [{
"id": 17,
"name": "Bourbon Family : PKT",
"activity_id": 30
}, {
"id": 18,
"name": "Choluv jar : JAR",
"activity_id": 30
}]
}
}]
All I want is to put the data that has the same poi_id under one collection with the key of poi_id and same for the items with same due_date.
Here's what I've done so far:
let activityArray = {};
_.forEach(activities, (activityItem) => {
if (!activityArray[activityItem.poi_id]) {
activityArray[activityItem.poi_id] = [];
}
activityArray[activityItem.poi_id].push(activityItem);
_.forEach(activityArray[activityItem.poi_id], (value, key) => {
activityArray[activityItem.poi_id][key].items.unshift(activityArray[activityItem.poi_id][key].due_date);
});
});
And this is what I got:
"1": [{
"id": 32,
"poi_id": 1,
"due_date": "2016-09-08T18:15:00.000Z",
/*unShift added due_date twice here, I want here key value pair*/
"items": [
"2016-09-08T18:15:00.000Z",
"2016-09-08T18:15:00.000Z", {
"id": 21,
"name": "Choluv jar : JAR",
"activity_id": 32
}
]
}, {
"id": 29,
"poi_id": 1,
"due_date": "2016-09-27T18:15:00.000Z",
"items": [
"2016-09-27T18:15:00.000Z", {
"id": 16,
"name": "Choluv jar : JAR",
"activity_id": 29
}
]
}],
"9": [{
"id": 30,
"poi_id": 9,
"due_date": "2016-09-14T18:15:00.000Z",
"items": [
"2016-09-14T18:15:00.000Z", {
"id": 17,
"name": "Bourbon Family : PKT",
"activity_id": 30
}, {
"id": 18,
"name": "Choluv jar : JAR",
"activity_id": 30
}
]
}]
I tried with other approaches too, but couldn't make it like the one I'm expecting.
Please guide me here.
Thanks.
A compact solution in plain Javascript with an object as hash for the items arrays.
var activities = [{ "id": 32, "poi_id": 1, "due_date": "2016-09-08T18:15:00.000Z", "items": [{ "id": 21, "name": "Choluv jar : JAR", "activity_id": 32 }] }, { "id": 30, "poi_id": 9, "due_date": "2016-09-14T18:15:00.000Z", "items": [{ "id": 17, "name": "Bourbon Family : PKT", "activity_id": 30 }, { "id": 18, "name": "Choluv jar : JAR", "activity_id": 30 }] }, { "id": 29, "poi_id": 1, "due_date": "2016-09-27T18:15:00.000Z", "items": [{ "id": 16, "name": "Choluv jar : JAR", "activity_id": 29 }] }],
hash = {},
grouped = {};
activities.forEach(a => {
hash[a.poi_id] = hash[a.poi_id] || {};
hash[a.poi_id][a.due_date] = hash[a.poi_id][a.due_date] || [];
grouped[a.poi_id] = grouped[a.poi_id] || [];
grouped[a.poi_id].push({
id: a.id,
poi_id: a.poi_id,
due_date: a.due_date,
items: { [a.due_date]: hash[a.poi_id][a.due_date] }
});
a.items.forEach(b => hash[a.poi_id][a.due_date].push(b));
});
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }