Related
I have an array of objects in an array. Each object has a date field. Here is a method I wrote to retrieve the index of the object with the newest date, works fine:
GetIndexOfLatestDate()
{
var indexOfLatestDate:number = 0;
var maxDate:number = new Date(this.objArray[0].date).getTime();
for(var nIndex:number = 1; nIndex < this.m_objArray.length; nIndex++)
{
if(new Date(this.objArray[nIndex].date).getTime() > maxDate)
{
maxDate = new Date(this.objArray[nIndex].date).getTime();
indexOFLatestDate = nIndex;
}
}
return indexOfLatestDate;
}
How can this be written (much) more succinctly?
Thanks for any help.
I would suggest using the reduce function that javascript provides. This solution also doesn't loop through the array multiple times, and it calls new Date().getTime() once per date.
GetIndexOfLatestDate()
{
if (this.objectArr === null || this.objectArr.length === 0) {
return null;
}
return this.objectArr.reduce((accum, value, index) => {
const newDate = new Date(value.date).getTime();
return newDate > accum.maxDate ? {index, maxDate: newDate} : accum;
}, {index: 0, maxDate: new Date(this.objectArr[0].date).getTime()}).index;
}
if this looks too confusing, here is an expanded version that's easier to follow if you are new to the reduce function.
GetIndexOfLatestDate()
{
// check if object arr is empty
if (this.objectArr === null || this.objectArr.length === 0) {
return null;
}
// set default accumulator for first passthrough
const defaultAccum = {
index: 0,
maxDate: new Date(this.objectArr[0].date).getTime()
}
const maxValueWithIndex = this.objectArr.reduce((accum, value, index) => {
// set formatted date to prevent multiple Date() calls
const newDate = new Date(value.date).getTime();
// if the new date is larger than the current largest date, set
// the accumulator to the new largest date and its index
if (newDate > accum.maxDate)
accum = {
index: index,
maxDate: newDate
};
}
// return the current accumulator, i.e. the current largest date
return accum;
}, defaultAccum);
// return the index of the latest date
return maxValueWithIndex.index;
}
You can do this using built-in function like this
const array1 = [{date: '2/5/2021'}, {date: '3/11/2019'}, {date: '12/9/2022'}];
const dateArray = array1.map(({date}) => {return new Date(date)})
const maxDate = Math.max(...dateArray);
const indexMaxElem = dateArray.findIndex(dateObj => dateObj.getTime() === maxDate)
console.log(indexMaxElem)
It is less efficient though, since it needs to do multiple pass through the array
let dateArr = [];
objArray.forEach(item => {
// extract the dates from the source array to form new array
dateArr.push(objArray.date.getTime();
});
// find the maximum date in this array, which will have the same index
indexOfLatest = dateArr.findIndex(Math.max(...dateArr));
GetIndexOfLatestDate(objArray){
let max = objArray.reduce(function (a, b){ return new Date(a.date) > new
Date(b.date) ? a : b; });
return objArray.indexOf(max);
}
You can do it with a reduce, something like:
index = this.objArray.reduce((accum, value, index) => {
if(!accum){
accum = {
index,
maxDate: value.date
};
} else {
if(accum.maxDate.getTime() > value.date.getTime()){
accum = {
index,
maxDate: value.date
};
}
}
return accum;
}
}, null).index;
How can I sort this array by date (ISO 8601)?
var myArray = new Array();
myArray[0] = { name:'oldest', date:'2007-01-17T08:00:00Z' }
myArray[1] = { name:'newest', date:'2011-01-28T08:00:00Z' }
myArray[2] = { name:'old', date:'2009-11-25T08:00:00Z' }
Playground:
https://jsfiddle.net/4tUZt/
Sort Lexicographically:
As #kdbanman points out, ISO8601See General principles was designed for lexicographical sort. As such the ISO8601 string representation can be sorted like any other string, and this will give the expected order.
'2007-01-17T08:00:00Z' < '2008-01-17T08:00:00Z' === true
So you would implement:
var myArray = [
{ name:'oldest', date:'2007-01-17T08:00:00Z' },
{ name:'newest', date:'2011-01-28T08:00:00Z' },
{ name:'old', date:'2009-11-25T08:00:00Z' }
];
myArray.sort(function(a, b) {
return (a.date < b.date) ? -1 : ((a.date > b.date) ? 1 : 0);
});
Sort using JavaScript Date:
Older versions of WebKit and Internet Explorer do not support ISO 8601 dates, so you have to make a compatible date. It is supported by FireFox, and modern WebKit though See here for more information about Date.parse support JavaScript: Which browsers support parsing of ISO-8601 Date String with Date.parse
Here is a very good article for creating a Javascript ISO 8601 compatible date, which you can then sort like regular javascript dates.
http://webcloud.se/log/JavaScript-and-ISO-8601/
Date.prototype.setISO8601 = function (string) {
var regexp = "([0-9]{4})(-([0-9]{2})(-([0-9]{2})" +
"(T([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?" +
"(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?";
var d = string.match(new RegExp(regexp));
var offset = 0;
var date = new Date(d[1], 0, 1);
if (d[3]) { date.setMonth(d[3] - 1); }
if (d[5]) { date.setDate(d[5]); }
if (d[7]) { date.setHours(d[7]); }
if (d[8]) { date.setMinutes(d[8]); }
if (d[10]) { date.setSeconds(d[10]); }
if (d[12]) { date.setMilliseconds(Number("0." + d[12]) * 1000); }
if (d[14]) {
offset = (Number(d[16]) * 60) + Number(d[17]);
offset *= ((d[15] == '-') ? 1 : -1);
}
offset -= date.getTimezoneOffset();
time = (Number(date) + (offset * 60 * 1000));
this.setTime(Number(time));
}
Usage:
console.log(myArray.sort(sortByDate));
function sortByDate( obj1, obj2 ) {
var date1 = (new Date()).setISO8601(obj1.date);
var date2 = (new Date()).setISO8601(obj2.date);
return date2 > date1 ? 1 : -1;
}
Updated usage to include sorting technique credit #nbrooks
You can avoid creating of dates and by using the built–in lexicographic compare function String.prototype.localeCompare, rather than the ?: compound operator or other expressions:
var myArray = [
{name: 'oldest', date: '2007-01-17T08:00:00Z'},
{name: 'newest', date: '2011-01-28T08:00:00Z'},
{name: 'old', date: '2009-11-25T08:00:00Z'}
];
// Oldest first
console.log(
myArray.sort((a, b) => a.date.localeCompare(b.date))
);
// Newest first
console.log(
myArray.sort((a, b) => -a.date.localeCompare(b.date))
);
Be careful, the accepted answer now advises to sort our dates lexicographically.
However, this will only work if all your strings use the 'Z' or '+00' timezone (= UTC).
Date strings ending with 'Z' do satisfy ISO8601 standard, but all ISO8601 do not end with 'Z'.
Thus, to be fully ISO8601 compliant, you need to parse your strings with some Date library (e.g. Javascript Date or Moment.js), and compare these objects.
For this part, you can check Scott's answer that also covers browsers incompatible with ISO8601.
My simple example with Javascript Date (works on any not-too-old browser) :
var myArray = [
{ name:'oldest', date:'2007-01-17T08:00:00Z' },
{ name:'newest', date:'2011-01-28T08:00:00+0100' },
{ name:'old', date:'2009-11-25T08:00:00-0100' }
];
myArray.sort(function(a, b) {
return new Date(a.date) - new Date(b.date);
});
Downside : This is slower than just comparing strings lexicographically.
More info about ISO8601 standard : here.
I'd go with this:
const myArray = new Array();
myArray[0] = { name:'oldest', date:'2007-01-17T08:00:00Z' }
myArray[1] = { name:'newest', date:'2011-01-28T08:00:00Z' }
myArray[2] = { name:'old', date:'2009-11-25T08:00:00Z' }
function byDate (a, b) {
if (a.date < b.date) return -1;
if (a.date > b.date) return 1;
return 0;
}
const newArray = myArray.sort(byDate);
console.clear();
console.dir(myArray);
console.dir(newArray);
http://jsfiddle.net/4tUZt/2/
$(document).ready(function()
{
var myArray = [ { name:'oldest', date:'2007-01-17T08:00:00Z' },
{ name:'newest', date:'2011-01-28T08:00:00Z' },
{ name:'old', date:'2009-11-25T08:00:00Z' }];
console.log( myArray.sort(sortByDate) );
});
// Stable, ascending sort (use < for descending)
function sortByDate( obj1, obj2 ) {
return new Date(obj2.date) > new Date(obj1.date) ? 1 : -1;
}
Demo: http://jsfiddle.net/4tUZt/4/
var myArray = new Array();
myArray[0] = { name:'oldest', date: '2007-01-17T08:00:00Z' };
myArray[1] = { name:'newest', date: '2011-01-28T08:00:00Z' };
myArray[2] = { name:'old', date: '2009-11-25T08:00:00Z' };
var sortFunction = function (a, b) {
return Date.parse(b.date) - Date.parse(a.date);
};
/* or
var sortFunction = function (a, b) {
return new Date(b.date) - new Date(a.date);
};
*/
console.log(myArray.sort(sortFunction));
ISO8601 is designed to sort correctly as plain text, so in general, a normal sort will do.
To sort by a specific key of objects in an array, you need to specify a comparison function to the sort() method. In many other languages, these are easy to write using the cmp function, but JS doesn't have a built in cmp function, so I find it easiest to write my own.
var myArray = new Array();
myArray[0] = { name:'oldest', date:'2007-01-17T08:00:00Z' }
myArray[1] = { name:'newest', date:'2011-01-28T08:00:00Z' }
myArray[2] = { name:'old', date:'2009-11-25T08:00:00Z' }
// cmp helper function - built in to many other languages
var cmp = function (a, b) {
return (a > b) ? 1 : ( (a > b) ? -1 : 0 );
}
myArray.sort(function (a,b) { return cmp(a.date, b.date) });
P.s. I would write my array using JSON-like syntax, like this:
var myArray = [
{ name:'oldest', date:'2007-01-17T08:00:00Z' },
{ name:'newest', date:'2011-01-28T08:00:00Z' },
{ name:'old', date:'2009-11-25T08:00:00Z' }
];
In the instance that you're sorting objects that may be missing a date, and dates may be in different timezones, you'll end up needing something a little more complex:
const deletionDateSortASC = (itemA, itemB) =>
(+new Date(itemA.deletedAt) || 0) -
(+new Date(itemB.deletedAt) || 0);
const deletionDateSortDESC = (itemA, itemB) =>
deletionDateSortASC(itemB, itemA);
If you know the dates are all defined and valid, and you know that all the dates are in the same timezone, then you should pick one of the other faster answers. However, if you want date sorting, have one or more of these edge cases, and don't want to have to preprocess the data to clean it up, then I suggest this approach.
I tried to demonstrate in the snippet below how the other answers fail in these edge cases.
const data = [
{deletedAt: null},
{deletedAt: '2022-08-24T12:00:00Z'},
{deletedAt: undefined},
{deletedAt: '2015-01-01T00:00:00Z'},
{deletedAt: '2022-08-24T12:00:00-01:00'},
{deletedAt: '2022-08-24T12:00:00+01:00'},
{deletedAt: '2022-08-20T12:00:00+01:00'},
{deletedAt: undefined}
];
const deletionDateSortASC = (itemA, itemB) =>
(+new Date(itemA.deletedAt) || 0) -
(+new Date(itemB.deletedAt) || 0);
const deletionDateSortDESC = (itemA, itemB) =>
deletionDateSortASC(itemB, itemA);
function acceptedAnswerSortASC(a, b) {
return (a.deletedAt < b.deletedAt) ? -1 : ((a.deletedAt > b.deletedAt) ? 1 : 0);
}
function acceptedAnswerSortDESC(a, b) {
return acceptedAnswerSortASC(b, a);
}
// Had to modify this solution to avoid the TypeError: a.deletedAt is null
const localeCompareSortASC = (a, b) => (a.deletedAt || '').localeCompare(b.deletedAt);
const localeCompareSortDESC = (a, b) => -(a.deletedAt || '').localeCompare(b.deletedAt);
function simpleDateSubtractionSortASC(a, b) {
return new Date(a.deletedAt) - new Date(b.deletedAt);
}
function simpleDateSubtractionSortDESC(a, b) {
return simpleDateSubtractionSortASC(b, a);
}
console.log('Using modified Date subtraction', [...data].sort(deletionDateSortDESC));
console.log('Using accepted answer lexocographical sort', [...data].sort(acceptedAnswerSortDESC));
console.log('Using locale compare lexocographical sort', [...data].sort(localeCompareSortDESC));
console.log('Using simple Date subtraction sort', [...data].sort(simpleDateSubtractionSortDESC));
Hi I'm having an array of the date object
"["2021-01-05T06:30:00.000Z","2021-01-06T06:30:00.000Z",
"2021-01-20T06:30:00.000Z","2021-02-09T06:30:00.000Z",
"2021-02-23T06:30:00.000Z","2021-02-16T06:30:00.000Z",
"2020-12-08T06:30:00.000Z","2020-12-15T06:30:00.000Z",
"2020-12-02T06:30:00.000Z","2020-12-09T06:30:00.000Z",
"2020-12-16T06:30:00.000Z"]"
I need to format into this
[
{
"month": "12",
"year": "2020",
"dates": [1,14,25]
},
{
"month": "10",
"year": "2020",
"dates": [1]
}
]
How to format like this help me. I have done like this but not completed I was stuck in adding dates. I know this is not the correct way of doing it. Please don't bother the code I have written I know it's garbage.
dateArray.reduce((initial,next)=>{
let result=[]
if(isSameYear(new Date(initial),new Date(next) &&
isSameMonth(new Date(initial),new Date(next))){
result.push({
month:new Date(nex).getMonth(),
year: new Date(next).getFullYear
})
}
})
You can group dates based on year and month in an object accumulator.
const data = ["2021-01-05T06:30:00.000Z", "2021-01-06T06:30:00.000Z", "2021-01-20T06:30:00.000Z", "2021-02-09T06:30:00.000Z", "2021-02-23T06:30:00.000Z", "2021-02-16T06:30:00.000Z", "2020-12-08T06:30:00.000Z", "2020-12-15T06:30:00.000Z", "2020-12-02T06:30:00.000Z", "2020-12-09T06:30:00.000Z", "2020-12-16T06:30:00.000Z" ],
result = Object.values(data.reduce((r, date) => {
const [year, month, day] = date.substr(0,10).split('-');
const key = `${year}_${month}`;
r[key] = r[key] || {month, year, dates: []};
r[key].dates.push(day);
return r;
},{}));
console.log(result);
When you group things in general, it's easier to group them into an object. The reason is you don't have to search an array for a matching result to append to, you only have to look up a key to concatenate to.
Here's one solution that builds an object, grouped by string keys built out of the month and year, and then maps over the values of that object to build the array you're looking for, by splitting the string keys into their significant parts.
const dates = ["2021-01-05T06:30:00.000Z","2021-01-06T06:30:00.000Z","2021-01-20T06:30:00.000Z","2021-02-09T06:30:00.000Z","2021-02-23T06:30:00.000Z","2021-02-16T06:30:00.000Z","2020-12-08T06:30:00.000Z","2020-12-15T06:30:00.000Z","2020-12-02T06:30:00.000Z","2020-12-09T06:30:00.000Z","2020-12-16T06:30:00.000Z"];
const grouped = dates.reduce((accumulator, date) => {
const parsed = new Date(date);
const year = parsed.getFullYear();
const month = parsed.getMonth();
const groupKey = `${month},${year}`;
accumulator[groupKey] = accumulator[groupKey] || {dates: []};
accumulator[groupKey].dates.push(parsed.getDay());
return accumulator;
}, {});
const result = Object.entries(grouped).map(([key, dates]) => {
const parts = key.split(',');
return {
month: parts[0],
year: parts[1],
dates: dates
};
});
console.log(result);
maybe do it in two passes
const dateArray = ["2021-01-05T06:30:00.000Z", "2021-01-06T06:30:00.000Z", "2021-01-20T06:30:00.000Z", "2021-02-09T06:30:00.000Z", "2021-02-23T06:30:00.000Z", "2021-02-16T06:30:00.000Z", "2020-12-08T06:30:00.000Z", "2020-12-15T06:30:00.000Z", "2020-12-02T06:30:00.000Z", "2020-12-09T06:30:00.000Z", "2020-12-16T06:30:00.000Z"];
const mapping = dateArray.reduce((initial, next) => {
const month = next.substring(5, 7);
const year = next.substring(0, 4);
const day = next.substring(8, 10);
initial[year] = initial[year] || {};
initial[year][month] = initial[year][month] || [];
initial[year][month].push(parseInt(day, 10));
return initial;
}, {});
const result = []
Object.keys(mapping).forEach(year => {
Object.keys(mapping[year]).forEach(month => {
result.push({
month,
year,
dates: mapping[year][month]
});
});
});
console.log(result);
One simple solution is to use an object to group by month and year like below:
const data = ["2021-01-05T06:30:00.000Z","2021-01-06T06:30:00.000Z",
"2021-01-20T06:30:00.000Z","2021-02-09T06:30:00.000Z",
"2021-02-23T06:30:00.000Z","2021-02-16T06:30:00.000Z",
"2020-12-08T06:30:00.000Z","2020-12-15T06:30:00.000Z",
"2020-12-02T06:30:00.000Z","2020-12-09T06:30:00.000Z",
"2020-12-16T06:30:00.000Z"];
function groupDates(dates) {
const groupedDates = {};
dates.forEach(d => {
const dt = new Date(d);
const date = dt.getDate();
const year = dt.getFullYear();
const month = dt.getMonth() + 1;
const key = `${year}-${month}`;
if (key in groupedDates) {
groupedDates[key].dates = [...groupedDates[key].dates, date];
} else {
groupedDates[key] = {
year,
month,
dates: [date],
};
}
});
return Object.values(groupedDates);
}
console.log(groupDates(data));
Here is a pure javascript solution without using any library. It is based on a simple O(n^2) runtime. But if you like to use some libraries for like binary search you can reduce it to O(nlogn).
The trick is to brick this task into smaller task as I did with functions getMonthYear (to convert string to object), compare and addDate:
data = ["2021-01-05T06:30:00.000Z","2021-01-06T06:30:00.000Z","2021-01-20T06:30:00.000Z","2021-02-09T06:30:00.000Z","2021-02-23T06:30:00.000Z","2021-02-16T06:30:00.000Z","2020-12-08T06:30:00.000Z","2020-12-15T06:30:00.000Z","2020-12-02T06:30:00.000Z","2020-12-09T06:30:00.000Z","2020-12-16T06:30:00.000Z"];
function categorize(data) {
// 2021-01-05T06:30:00.000Z => {month:"01", year:"2021", date:"05"}
function getMonthYear(str) {
var datePart = str.toString().trim().split("T")[0];
var datePartArr = datePart.split("-");
return {month:datePartArr[1], year:datePartArr[0], date:datePartArr[2]};
}
// testing
//var ans = getMonthYear("2021-01-06T06:30:00.000Z");
//console.log(ans);
// comparing two items to see if they have the same year and month
function compare(item1, item2) {
return (item1.month == item2.month) && (item1.year == item2.year);
}
// testing
//var ans = compare({month:"04", year:"2021"}, {month:"03", year:"2021"});
//console.log(ans);
// adding a date to the list of dates
function addDate(dateList, dateNumber) {
for(var i in dateList) {
if (dateList[i] == dateNumber) return;
}
dateList.push(dateNumber);
}
// testing
/*var ans = [2,4];
addDate(ans, 4);
console.log(ans);*/
// Now lets build the answer by looping over
// --------------------------------------------
var list = []; // the final answer list
data.forEach(function(str){
var item = getMonthYear(str);
var itemMatched = false;
// now lopping over the list to see if it has any match
for(var i in list) {
if (compare(item, list[i])) { // matched found
itemMatched = true;
addDate(list[i].date, item.date);
break;
}
}
// item had no match, add it as a new item to list
if (!itemMatched) {
list.push({
month: item.month,
year: item.year,
date: [item.date]
});
}
});
return list;
}
var ans = categorize(data);
console.log(ans);
Here is link to jsfiddle
I'm trying to split a sorted array containing ISO 8601 (UTC) dates into a 2D array, where each day represents an array containing all of the dates where the year/month/day match.
Example array:
let dates = [
'2019-07-14T08:02:27Z',
'2019-07-05T17:22:34Z',
'2019-06-25T21:23:30Z',
'2019-06-25T16:46:55Z',
'2019-06-18T14:14:15Z',
'2019-06-18T13:09:07Z'
];
Ideally, the above array would create the following 2D array:
let dates = [
['2019-07-14T08:02:27Z'],
['2019-07-05T17:22:34Z'],
['2019-06-25T21:23:30Z', '2019-06-25T16:46:55Z'],
['2019-06-18T14:14:15Z', '2019-06-18T13:09:07Z']
];
I've tried quite a few approaches but for the sake of providing some code, I substring each array element date[i].substring(0, 10) which returns the year/month/day, say 2019-07-14 - since the array is sorted I compare the previous and current element and decide if the element should be added to a new array or not.
let arr = [], temp = [];
for (let i = 0, prev = null; i < dates.length; i++) {
temp.push(dates[i]);
if (!(dates[i].substring(0, 10) === prev)) {
arr.push(temp);
temp = [];
}
prev = dates[i].substring(0, 10);
}
Code doesn't work as it is off by one in some cases, e.g:
[ '2019-07-14T08:02:27Z' ],
[ '2019-07-10T14:22:04Z' ],
[ '2019-07-09T16:08:22Z' ], // should be in below array
[
'2019-07-09T15:58:55Z',
'2019-07-09T14:41:49Z',
'2019-07-09T14:12:04Z',
'2019-07-09T14:10:29Z',
'2019-07-09T13:34:46Z',
'2019-07-09T13:28:14Z',
'2019-07-08T15:51:38Z' // conflicting dates
],
[ '2019-07-08T15:45:07Z', '2019-07-07T15:07:47Z' ], // conflicting dates
Any suggestions?
You could reduce the array by checking the date substring.
let dates = ['2019-07-14T08:02:27Z', '2019-07-05T17:22:34Z', '2019-06-25T21:23:30Z', '2019-06-25T16:46:55Z', '2019-06-18T14:14:15Z', '2019-06-18T13:09:07Z'],
result = dates.reduce((r, s, i) => {
if (!i || r[r.length - 1][0].slice(0, 10) !== s.slice(0, 10)) r.push([]);
r[r.length - 1].push(s);
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Simply maintain a map of all the dates, and keep pushing the dates to the array if they are present in the map, Object.values() on the map will give you the desired result, This works fine even when the dates are not sorted:
let dates = [ '2019-07-14T08:02:27Z', '2019-07-05T17:22:34Z', '2019-06-25T21:23:30Z', '2019-06-25T16:46:55Z', '2019-06-18T14:14:15Z', '2019-06-18T13:09:07Z' ];
let resullt = Object.values(dates.reduce((acc,d) => {
let date = d.substr(0,10);
(acc[date] = acc[date] || []).push(d);
return acc;
},{}));
console.log(resullt);
let arr = [], temp = [];
for (let i = 0, prev = null; i < dates.length; i++) {
if (
prev &&
!(dates[i].substring(0, 10) === prev)
) {
arr.push(temp);
temp = [];
}
temp.push(dates[i]);
prev = dates[i].substring(0, 10);
}
arr.push(temp);
You could use a function generator to group the dates according to a desired criteria.
This likely is going to be less compact than other solutions, but should be easier to maintain and to read.
Below example acquires two dates at a time and checks whether the current one (first) satisfy the provided criteria against the next one. If it does, it accumulates it and continues, otherwise it yield the previous block and continues the cycle until finished.
Below is the implementation:
let dates = [
'2019-07-14T08:02:27Z',
'2019-07-05T17:22:34Z',
'2019-06-25T21:23:30Z',
'2019-06-25T16:46:55Z',
'2019-06-18T14:14:15Z',
'2019-06-18T13:09:07Z'
];
function* groupSimilarDates(dateArr, criteria) {
var acc = [];
for (var i = 0; i < dateArr.length; i++) {
var [currDate, nextDate] = [dateArr[i],dateArr[i+1]];
if (nextDate) {
if (criteria(currDate, nextDate)) acc.push(currDate);
else {
if (acc.length) {
acc.push(currDate);
yield acc, acc = [];
}
else yield [currDate];
}
}
else {
if (criteria(currDate, dateArr[i-1])) acc.push(currDate), yield acc;
else {
if (acc.length) yield [acc];
yield [currDate];
}
}
}
}
console.log([...groupSimilarDates(dates, (a,b) => a.substring(0,10) === b.substring(0,10))]);
I have an array of objects with example values
[
{name:"Name1",date:"2018-08-01", optimalValue:"33", realValue:"55"},
{name:"Name2",date:"2018-08-03", optimalValue:"17", realValue:"23"},
{name:"Name3",date:"2018-08-01", optimalValue:"23", realValue:"12"},
{name:"Name4",date:"2018-08-04", optimalValue:"12", realValue:"11"},
]
I want to find in this array element with same date and sum values in optimalValue and realValue and have similar array like this:
[
{date:"2018-08-01", optimalValue:"56", realValue:"77"},
{date:"2018-08-03", optimalValue:"17", realValue:"23"},
{date:"2018-08-04", optimalValue:"12", realValue:"11"},
]
Just use a hashtable to find / group duplicates:
const hash = {}, result = [];
for(const { date, name, optimalValue, realValue } of input) {
if(!hash[date])
result.push(hash[date] = { date, name, optimalValue: 0, realValue: 0 });
hash[date].realValue +=+ realValue;
hash[date].optimalValue +=+ optimalValue;
}
You can use the function reduce for grouping and sum the values and the function Object.values to extract the desired array.
let array = [{name:"Name1",date:"2018-08-01", optimalValue:"33", realValue:"55"},{name:"Name2",date:"2018-08-03", optimalValue:"17", realValue:"23"},{name:"Name3",date:"2018-08-01", optimalValue:"23", realValue:"12"},{name:"Name4",date:"2018-08-04", optimalValue:"12", realValue:"11"},],
result = Object.values(array.reduce((a, {name, date, optimalValue, realValue}) => {
a[date] = (a[date] || {date, optimalValue: 0, realValue: 0});
a[date].realValue = String(Number(a[date].realValue) + Number(realValue));
a[date].optimalValue = String(Number(a[date].optimalValue) + Number(optimalValue));
return a;
}, {}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use Array.reduce() for that. Since you require optimalValue and realValue as a string type, you can convert those into string after summing up the value using parseInt():
var arr = [
{name:"Name1",date:"2018-08-01", optimalValue:"33", realValue:"55"},
{name:"Name2",date:"2018-08-03", optimalValue:"17", realValue:"23"},
{name:"Name3",date:"2018-08-01", optimalValue:"23", realValue:"12"},
{name:"Name4",date:"2018-08-04", optimalValue:"12", realValue:"11"},
];
var res = arr.reduce((acc, obj)=>{
var existObj = acc.find(item => item.date === obj.date);
if(existObj){
existObj.realValue = (parseInt(existObj.realValue) + parseInt(obj.realValue)).toString();
existObj.optimalValue = (parseInt(existObj.optimalValue) + parseInt(obj.optimalValue)).toString();
return acc;
}
acc.push(obj);
return acc;
},[]);
console.log(res);