Group array of objects by multiple properties with Lodash - javascript

is possible with lodash library group elements by 2 properties?
I have array of objects like this:
[{
id: 1,
amount: 2000,
date: "2018-01-31T00:00:00.000Z"
},{
id: 2,
amount: 3000,
date: "2017-07-31T00:00:00.000Z"
},{
id: 3,
amount: 6000,
date: "2018-01-31T00:00:00.000Z"
},{
id: 4,
amount: 7000,
date: "2017-01-31T00:00:00.000Z"
},{
id: 5,
amount: 5000,
date: "2017-03-31T00:00:00.000Z"
},{
id: 6,
amount: 3000,
date: "2018-02-22T00:00:00.000Z"
},{
id: 7,
amount: 4500,
date: "2017-01-31T00:00:00.000Z"
}]
My goal is group objects in array by:
year
month
Purpose of that grouping is that I need in result order these objects' sum of amount by newest. So for that reason I need distinguish January 2017 from January 2018. It should be 2 different groups.
I am not sure if my approach is correct so I write here my required output:
[
3000, // sum of 2018-2
8000, // sum of 2018-1
3000 // sum of 2017-7
5000 // sum of 2017-3
11500 // sum of 2017-1
]
I tried following command but it doesn't work and give me error:
let xxx = _(data)
.groupBy(function(i) {
new Date(i.date).getFullYear()
})
.groupBy(function(i) {
new Date(i.date).getMonth()
})
.map(x => x.amount)
.sum()
.orderBy('date').value();
Can you help me to fix it ? Thanks.

You can just concat your year and month with groupBy and use it.
var grouped = _.groupBy(data, function(i) {
return new Date(i.date).getFullYear()+'-'+new Date(i.date).getMonth()
})
var resultUnsorted = _.map(t, (val, key) => ({key: key, val: val.reduce((p, c) => c.amount + p, 0) }));
then sort using _.orderBy
const output = _.orderBy(resultUnsorted, 'key');
you can write your custom sort function using the behaviour you want.

Related

How to combine 2 or more API sources into one table (React, HTML)

So I have 3 API sources that return data in this format
firstSource = [{month, endDate, expectedPrice}] ->array of 12 objects
secondSource = [{month, value}] ->array of 12 objects
thirdSource = [{month, value}] ->array of 12 objects
example:
secondSource = [
{month: 1, value: 234.22},
{month: 2, value: 123.58},
{month: 3, value: 255.35},
...
...
you get the idea
]
I've come across this solution that uses lodash library. This is all well, but I'm interested in a solution that doesn't require external libraries. So basically, I want to achieve the same thing the person from the lodash solution did -> combine all three data sources into one and then use that to display the data in <table> element, only without using external libraries, e.g. lodash.
Expected output:
combined = [
{month, endDate, expectedPrice, secondSourceValue, thirdSourceValue}
]
Any ideas?
what's the problem to try something like this?
let combinedData = []
firstSource.forEach((element, index) => {
combinedData[index] = {
month: element.month,
endDate: element.endDate,
expectedPrice: element.expectedPrice,
secondSourceValue: secondSource[index].value,
thirdSourceValue: thirdSource[index].value
}
});
You only have to check if the second and third sources have the same length with the main.
We can use array.mapto merge multiple arrays of objects and create a new array with properties of all objects.
Do note that, in this case, the length of all the arrays should be the same.
let arr1 = [{
month: 1,
value: 234.22,
date: 'JAN'
},
{
month: 2,
value: 123.58,
date: 'FEB'
},
{
month: 3,
value: 255.35,
date: 'MARCH'
}
]
let arr2 = [{
month: 1,
property: 1
},
{
month: 2,
property: 2
},
{
month: 3,
property: 3
}
]
let arr3 = [{
month: 1,
property: 9
},
{
month: 2,
property: 8
},
{
month: 3,
property: 7
},
]
let combinedArr = arr1.map((item, index) => {
return {
...item,
...arr2[index],
...arr3[index]
}
})
console.log(combinedArr)

JavaScript Equivalent to C# LINQ or another way to fetch data [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 2 years ago.
Improve this question
I have the JS array something like that
var arr = [{
TimeEdit: "2020-10-29T10:45:00.21Z",
Date: "2020-10-29T00:00:00",
Parameters: [ { Id: 1, Value: 1}, { Id: 2, Value: 348 }]
},{
TimeEdit: "2020-10-29T10:43:52.22Z",
Date: "2020-10-29T00:00:00",
Parameters: [ { Id: 1, Value: 12}, { Id: 2, Value: 348 }]
},{
TimeEdit: "2020-10-30T10:47:12.33Z",
Date: "2020-10-30T00:00:00",
Parameters: [ { Id: 1, Value: 3}, { Id: 2, Value: 2 }]
}];
and I want to select from this array data with a unique "Date" sorted by "TimeEdit" so that at the output I can get this array
[{
TimeEdit: "2020-10-29T10:45:00.21Z",
Date: "2020-10-29T00:00:00",
Parameters: [ { Id: 1, Value: 1}, { Id: 2, Value: 348 }]
},{
TimeEdit: "2020-10-30T10:47:12.33Z",
Date: "2020-10-30T00:00:00",
Parameters: [ { Id: 1, Value: 3}, { Id: 2, Value: 2 }]
}];
In C# code I would do something like that:
var dates = arr.Select(r => r.Date).Distinct().ToList();
foreach (var date in dates)
{
WorkLog log = arr.Where(r => r.Datetime == date).OrderByDescending(r => r.TimeEdit).FirstOrDefault();
//to do some stuff here
}
So what is the best way to do so in JS?
JavaScript has a number of built-in functions that help you in working with arrays in a linq-ish way. Functions like map (equivalent of Select) and filter (equivalent of Where) come to mind. Check out MDN for docs.
Even though these can be very useful, it can be useful to use another library for working with arrays or objects. From my experience lodash is very handy for manipulating arrays. It has a chain method that you can use to express operations in a very linq-ish way.
const dates = _.chain(arr).map(r => r.Date).uniq().value();
dates.forEach(date => {
const log = _.chain(arr)
.filter(r => r.Datetime === date)
.sortBy(r => r.TimeEdit)
.reverse()
.head();
//to do some stuff here
});
You could mimic the behaviour with some own function for grouping, ordering, sorting and getting the first item of each group. Fo binding all together, you need a pipe function as well.
const
pipe = (...functions) => input => functions.reduce((acc, fn) => fn(acc), input),
groupBy = key => array => array.reduce((r, o) => {
var fn = typeof key === 'function' ? key : o => o[key],
temp = r.find(([p]) => fn(o) === fn(p));
if (temp) temp.push(o);
else r.push([o]);
return r;
}, []),
order = (...keys) => array => array.sort((a, b) => {
var result;
keys.some(k => result = a[k] > b[k] || -(a[k] < b[k]));
return result
}),
select = fn => array => array.map(fn),
first = array => array[0],
data = [{ TimeEdit: "2020-10-29T10:45:00.21Z", Date: "2020-10-29T00:00:00", Parameters: [{ Id: 1, Value: 1 }, { Id: 2, Value: 348 }] }, { TimeEdit: "2020-10-29T10:43:52.22Z", Date: "2020-10-29T00:00:00", Parameters: [{ Id: 1, Value: 12 }, { Id: 2, Value: 348 }] }, { TimeEdit: "2020-10-30T10:47:12.33Z", Date: "2020-10-30T00:00:00", Parameters: [{ Id: 1, Value: 3 }, { Id: 2, Value: 2 }] }],
result = pipe(
groupBy('Date'),
select(
pipe(
order('TimeEdit'),
first
)
)
)(data);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Group array of objects by common key (date string)

I have an array as below.
const list = [
{
date: "2020-01-01",
number: 3,
count: 2
},
{
date: "2020-01-01",
number: 3,
count: 2
},
{
date: "2020-01-01",
number: 3,
count: 2
},
{
date: "2020-01-02",
number: 4,
count: 1
},
{
date: "2020-01-02",
number: 4,
count: 1
}
]
And I want to sum number and count depends on same date. After that it should be merged in same date. Therefore I want to get the result as below.
const list = [{
totalNumber: 9,
totalCount: 6,
detail:[{
date: "2020-01-01",
number: 3,
count: 2
},
{
date: "2020-01-01",
number: 3,
count: 2
},
{
date: "2020-01-01",
number: 3,
count: 2
}]
},
{
totalNumber: 8,
totalCount: 2,
detail:[
{
date: "2020-01-02",
number: 4,
count: 1
},
{
date: "2020-01-02",
number: 4,
count: 1
}]
}
]
I know stackoverflow is not code writing site. But I made this from some data. But this is my last stage for completing my object. And I don't know how I can group like that. I'' appreciate if you help me to resolve this problem. Thank you so much for reading it.
To me, optimal approach is building Map (having necessary property as a key) with Array.prototype.reduce(), then extracting array of values (aggregated records) with Map.prototype.values():
const src = [{date:"2020-01-01",number:3,count:2},{date:"2020-01-01",number:3,count:2},{date:"2020-01-01",number:3,count:2},{date:"2020-01-02",number:4,count:1},{date:"2020-01-02",number:4,count:1}],
groupped = [...src
.reduce((acc, {date, number, count}) => {
const group = acc.get(date)
if(group){
group.totalNumber += number
group.totalCount += count
group.detail.push({date, number, count})
} else {
acc.set(
date,
{
date,
totalNumber: number,
totalCount: count,
detail: [{
date,
number,
count
}]
}
)
}
return acc
}, new Map)
.values()
]
console.log(groupped)
.as-console-wrapper{min-height:100%;}

How to sort array containing key value pair according to date

Below is array.
What I have to do is sort array according to latest date.
So basically if you look the code after sorting id:12 should come before id:23
I have tried
myArray.sort(function(a, b) {
return a.date- b.date;
});
but it is not working
0:
{ id: 23
name: "joe"
price: 2300
date: "2018-06-01T09:48:18.000Z"},
1:
{ id: 12
name: "ali"
price: 300
date: "2018-09-01T09:48:1i.000Z"},
the following example explains how to sort the array by dates
var array=[{'date':'2018-06-05T09:48:18.000Z'},{'date':'2018-06-01T09:48:18.000Z'},{'date':'2018-04-01T09:48:18.000Z'}];
array.sort(function(a,b){
//convert your string into dates
return new Date(a.date) - new Date(b.date);
});
console.log(array)
By using an ISO 8601 compliant date, you could take the strings directly for sorting with String#localeCompare.
var array = [{ id: 23, name: "joe", price: 2300, date: "2018-06-01T09:48:18.000Z" }, { id: 12, name: "ali", price: 300, date: "2018-09-01T09:48:1i.000Z" }];
array.sort(function(a, b) {
return b.date.localeCompare(a.date); // desc
});
console.log(array);

Creating arrays in JavaScript by comparing values in another array

In JavaScript, I'm trying to create arrays based on the values of another array and I'm having difficultly.
I've got an array of dates in string format (dates) e.g.
["30/09/2015", "31/10/2015", "30/11/2015", "31/12/2015"]
I've got an Object to represent multiple bank accounts (balancesSortByAccId) e.g.
Cash - (Array size: 3)
id: 1, date: "30/09/2015", balance: 30
id: 2, date: "31/10/2015", balance: 50
id: 3, date: "30/11/2015", balance: 100
Natwest - (Array size: 2)
id: 4, date: "30/11/2015", balance: 400
id: 5, date: "31/12/2015", balance: 200
Whilst looping through all the accounts in balancesSortByAccId, I want to be able to create an array for the balance at each date in the dates array i.e.
[30, 50, 100, null]
[null, null, 400, 200]
How could I achieve this?
UPDATE: jsfiddle code - https://jsfiddle.net/gx8bLehb/
The easiest way would be to transform your cash and natwest arrays into a hash sorted by date, something like balancesByDate:
var balancesByDate = _.groupBy(cash, function(entry) {return entry.date});
Then use an array map() function, e.g. from lodash to iterate the dates array and for each date look up the account line in the balancesByDate hash. From that line, return the balance property in the map function.
dates.forEach(function(date){
if (balancesByDate[date]) {
results.push(_.map(balancesByDate[date], function(line){
return line.balance;
}));
} else {
results.push(null);
}
});
However, you need to be aware that your dataset most likely could contain duplicate balances for a day, you should plan for that (my code returns an array for each day).
https://jsfiddle.net/hdpuuc5d/1/
A solution in plain javascript with a helper object for the dates:
var dates = ["30/09/2015", "31/10/2015", "30/11/2015", "31/12/2015"],
datesObj = dates.reduce(function (r, a, i) { r[a] = i; return r; }, {}),
balances = {
Cash: [
{ id: 1, date: "30/09/2015", balance: 30 },
{ id: 2, date: "31/10/2015", balance: 50 },
{ id: 3, date: "30/11/2015", balance: 100 }
],
Natwest: [
{ id: 4, date: "30/11/2015", balance: 400 },
{ id: 5, date: "31/12/2015", balance: 200 }
]
},
result = {};
Object.keys(balances).forEach(function (k) {
result[k] = Array.apply(null, { length: dates.length }).map(function () { return null; });
balances[k].forEach(function (a) {
result[k][datesObj[a.date]] = a.balance;
});
});
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');

Categories

Resources