Sort array with timestamps and merge similar dates - javascript

I am trying to take this array :
[
{
"date": a timestamp: June 20, 2020 at 7:32:42 PM UTC
"value": 3
..
}
..
]
and accomplish 3 things effeciently
Convert the timestamp to normal date and replace with the timestamp
Merge dates of the same day. ( so i add their values and set under a single day.
Sort the array when recent are first.
I have started with something like this :
array.sort(function(a,b){ return new Date(b.date) - new Date(a.date);
var salesDates = sales.map(function(element){element.date = new Date(element.date); return element }); });
Which will only sort, but i need to replace timestamp/date, sort and merge same dates elegantly and effeciently
Is it possible with only sort function ?

Here. First i group it with .reduce(). Then i sort it .sort(). After that i change the timestamp to a date format.
let arr = [
{
"date": 1594023899426,
"value": 3
},
{
"date": 1592423499234,
"value": 2
},
{
"date": 1594023899426,
"value": 1
}
];
let result = arr
.reduce((a, v) => {
let index = a.findIndex(el => el.date === v.date);
if (index !== -1) {
a[index].value += v.value;
return a;
}
a.push({
date: v.date,
value: v.value
});
return a;
}, [])
.sort((a, b) => b.date - a.date)
.map(el => {
el.date = new Date(el.date);
return el;
});
console.log(result);

Here's another approach to it:
let sortingReducer = (accumulator, obj) => {
// This is the merging logic
let existingObj = accumulator.find(
(compareObj) => {
return obj.date?.getDate() === compareObj.date?.getDate()
}
);
if (existingObj) {
existingObj.value += obj.value;
return accumulator;
}
// This is the sorting logic
const nextIndex = accumulator.findIndex(
(compareObj) => obj.date?.getTime() < compareObj.date?.getTime()
);
const index = nextIndex > -1 ? nextIndex : accumulator.length;
accumulator.splice(index, 0, obj);
return accumulator;
};
const input = [
{
date: new Date(),
value: 2,
},
{
date: new Date(new Date().setDate(1)),
value: 4,
},
{
date: new Date(new Date().setDate(1)),
value: 1,
},
{
date: new Date(new Date().setDate(2)),
value: 7,
},
];
const output = input.reduce(sortingReducer, []);
I've take some help from this answer: https://stackoverflow.com/a/50246275/1912288
I like the approach in the above answer, this is just a different approach to it.

Related

How to filter an array based on Month comparison and get Max Date() in Angular?

I have an array of objects with a date value. I want to filter the array based on the selectedDate and get the Max date in the list of dates. In the below code, I am filtering the array based on the month. Here I get 3 values after filtering, now I want to compare those values and get the MAX Date() value.
How can I do that in Angular or ES6 way?
let selectedDate = new Date();
let array = [{
"date": "2022-08-30T23:00:00Z",
"value": "4.0"
},
{
"date": "2022-08-28T23:00:00Z",
"value": "8.0"
},
{
"date": "2022-08-25T23:00:00Z",
"value": "2.0"
},
{
"date": "2022-07-25T23:00:00Z",
"value": "2.0"
}
];
let x = array.filter(d =>
new Date(d.date).getMonth() === selectedDate.getMonth() - 1
)
console.log(x)
Expected Output:
{
"date": "2022-08-30T23:00:00Z",
"value": "4.0"
}
You need to filter not only by month, but also by year.
Please do not use new in the loop, if possible.
const array = [{"date": "2022-08-30T23:00:00Z","value": "4.0"},{"date": "2022-08-28T23:00:00Z","value": "8.0"},{"date": "2022-08-25T23:00:00Z","value": "2.0"},{"date": "2022-07-25T23:00:00Z","value": "2.0"}];
const selectedDate = '2022-09-01T06:08:58.695Z' // new Date().toISOString();
const getYearMonth = (isoDateTime) => isoDateTime.slice(0,7)
const getMax = (data, targetDate) => {
const targetYearMonth = getYearMonth(targetDate);
const filtered = data.filter(({ date }) => getYearMonth(date) === targetYearMonth);
if (filtered.length === 0) return null;
if (filtered.length === 1) return filtered.at(0);
return filtered.reduce((max, cur) => max.date.localeCompare(cur.date) < 0 ? cur : max)
};
console.log(getMax(array, '2022-07-01T06:08:58.695Z'))
console.log(getMax(array, '2022-08-01T06:08:58.695Z'))
console.log(getMax(array, '2022-09-01T06:08:58.695Z'))
.as-console-wrapper { max-height: 100% !important; top: 0 }
let yourOutput = [
{
"date": "2022-08-30T23:00:00Z",
"value": "4.0"
},
{
"date": "2022-08-28T23:00:00Z",
"value": "8.0"
},
{
"date": "2022-08-25T23:00:00Z",
"value": "2.0"
}
];
//Sort by Date
yourOutput.sort((a, b) => new Date(a) > new Date(b));
//Get First Elem
if(yourOutput.length > 0) {
console.log(yourOutput[0])
}
I think you can use reduce function afterfilter to get the max.
Assuming we have 2 variables, selectedDate and array:
let max = array
.filter(d =>
new Date(d.date).getMonth() === selectedDate.getMonth() - 1
)
.reduce((max, current) => {
if (!max) return current;
let maxDate = new Date(max.date);
let currentDate = new Date(current.date);
return maxDate > currentDate? max: current;
}, null);

Creating an array of Objects which will contain 4 variables

Im trying to create an array of objects that will hold 4 variables, i managed to create an array of objects that holds 2 variables but i'm stuck right now.
I have this array:
Checking => Array [
Object {
"count": 3,
"serviceType": "manicure",
},
Object {
"count": 2,
"serviceType": "Learn JavaScript",
},
Object {
"count": 1,
"serviceType": "Learn React",
},
]
Coming out from this code(first part):
const arrayofServices = services; //services => state the holding the services
const servicesCounter = arrayofServices.reduce((counterObj, service) => {
if (counterObj.hasOwnProperty(service)) {
counterObj[service] += 1;
return counterObj;
}
return {
...counterObj,
[service]: 1
};
}, {});
console.log("Service Counter in UseEffect" ,servicesCounter);
setServiceObj(servicesCounter);
and this code:
const res = {};
const arrayOfValues = Object.values(serviceObj); //Counter values
const arrayOfKeys = Object.keys(serviceObj); //serviceType values
arrayOfKeys.forEach((key, i) => (res[key] = arrayOfValues[i]));
const dat = {... res};
const array = [];
for(const [key,value] of Object.entries(dat)){
array.push({serviceType: key, count: value});
}
console.log("Checking => ", array); //Descending Order.
What i want to do is to add to "array" another parameters so the form will look like this:
Object {
"count": 1,
"serviceType": "Learn React",
"voucherCode": "f34dty",
"expDate:" //pass here end of the month date
},
I have created an array of voucerCodes and a variable of the lastDay of moth
suggestions?
Try this
var date = new Date();
var lastDay = new Date(date.getFullYear(), date.getMonth() + 1, 0);
const voucherCode = ['dLHFVd',"6VqLgA","FgTRUp"];
var i = 0;
for(const [key,value] of Object.entries(dat)){
array.push({
serviceType: key,
count: value,
voucherCode: voucherCode[i],
expDate: lastDay.getDate(),
});
i++;
}

How to get unique date values from object array

I need to get all unique days of multiple date values in the format DD.MM.. In this example data, there are two values for the 24th of december:
const data = [
{ date: ISODate("2019-12-24T03:24:00Z") },
{ date: ISODate("2019-12-24T04:56:00Z") },
{ date: ISODate("2019-12-25T02:34:00Z") },
{ date: ISODate("2019-12-26T01:23:00Z") }
]
So the result should be
const result = [
'24.12.',
'25.12.',
'26.12.'
]
So first of all I'll map my data and split the values only for the dates:
const dates = data.map(d => d.date.toString().split('T')[0])
But how do I get the unique values and change the output format?
Update
I came up with this, but it looks very complicated...
data.map(d => {
const dateSplit = d.date.toString().split('T')[0].split('-')
return dateSplit[2] + '.' + dateSplit[1] + '.'
})
.filter((value, index, self) {
return self.indexOf(value) === index
})
It seems that ISODate returns a standard JS Date object. You can use Date.getDate() to get the day, and Date.getMonth() to get the month (0 based, so we need to add 1):
const data = [
{ date: new Date('2019-12-24T03:24:00Z') },
{ date: new Date('2019-12-24T04:56:00Z') },
{ date: new Date('2019-12-25T02:34:00Z') },
{ date: new Date('2019-12-26T01:23:00Z') }
]
const result = [...new Set(data.map(({ date: d }) =>
`${d.getDate()}.${d.getMonth() + 1}.`
))]
console.log(result)
Previous answer:
Use a regular expression to match the month and the day, and assign them to consts using destructuring. Assemble the string using template literal. Remove duplicates by assigning the values to a Set, and then spreading back to an array.
Note: Since I don't have access to the ISODate, I've removed it. I left .toString() although it's not needed in this example, but will be needed when used with ISODate.
const data = [
{ date: '2019-12-24T03:24:00Z' },
{ date: '2019-12-24T04:56:00Z' },
{ date: '2019-12-25T02:34:00Z' },
{ date: '2019-12-26T01:23:00Z' }
]
const pattern = /-([0-9]{2})-([0-9]{2})T/
const result = [...new Set(data.map(d => {
const [, mon, day] = d.date.toString().match(pattern)
return `${day}.${mon}.`;
}))]
console.log(result)
Use .filter() to filter through only values that are the first of their value.
//temporary function
const ISODate = (d) => d;
const data = [{
date: ISODate("2019-12-24T03:24:00Z")
},
{
date: ISODate("2019-12-24T04:56:00Z")
},
{
date: ISODate("2019-12-25T02:34:00Z")
},
{
date: ISODate("2019-12-26T01:23:00Z")
}
]
const dates = data.map(d => d.date.toString().split('T')[0].split("-").slice(1, 3).reverse().join(".") + ".")
console.log(dates.filter((v, i, a) => a.indexOf(v) === i));
You can do this pretty easily by using Array.reduce. Note that I converted ISODate to be Date since I don't have that class, but it should be the same concept.
const data = [
{ date: new Date("2019-12-24T03:24:00Z") },
{ date: new Date("2019-12-24T04:56:00Z") },
{ date: new Date("2019-12-25T02:34:00Z") },
{ date: new Date("2019-12-26T01:23:00Z") }
];
const result = data.reduce( (acc, curr) => {
if (acc.length > 0) {
const hasDate = acc.find(d => d.date.getMonth() === curr.date.getMonth() && d.date.getDate() === curr.date.getDate());
if (!hasDate) { acc.push(curr); }
} else {
acc.push(curr);
}
return acc;
}, []);
console.log(result);
I would use the uniq function in the Underscore.js library:
const data = [
{ date: ISODate("2019-12-24T03:24:00Z") },
{ date: ISODate("2019-12-24T04:56:00Z") },
{ date: ISODate("2019-12-25T02:34:00Z") },
{ date: ISODate("2019-12-26T01:23:00Z") }
];
let dates = _.uniq(data.map(d => d.date.toString().split('T')[0]));
A nice considerable way is:
const array = [1, 2, 6, 5,5, 5, 3, 7, 8];
const uniqueKeys = array.reduce((hashMap, value) => {
if (!hashMap[value]) {
hashMap[value] = true;
}
return hashMap;
}, {});
const uniqueValues = Object.keys(uniqueKeys);
console.log(uniqueValues);
It is nice because it iterates the array once, instead of x * x (a.k.a log(n) instead of log(n^2) as with .filter() example
const array = [1, 2, 6, 5,5, 5, 3, 7, 8];
const uniqueKeys = array.reduce((hashMap, value) => {
if (!hashMap[value]) {
hashMap[value] = true;
}
return hashMap;
}, {});
const uniqueValues = Object.keys(uniqueKeys);
console.log(uniqueValues);

How to get the minimum and maximum date and grouped it by id

I have this array of data
[{"id":1, "start":"2018-10-10", "end":"2018-11-10"},
{"id":1, "start":"2018-11-10", "end":"2018-12-10"},
{"id":2, "start":"2018-11-22", "end":"2018-11-30"}]
I wanted to get the minimum in the start and the maximum in the end.
My desired output would be
{"id":1, "start":"2018-10-10", "end":"2018-12-10"},
{"id":2, "start":"2018-11-22", "end":"2018-11-30"}
I tried doing like this:
data.sort((a,b) => a.start.toString().localeCompare(b.start))
What you are trying to do will require a custom solution where you merge two entries. In the below function I have iterated your array in chunks of same id first I have sorted it based on id and kept a min and max of the index where your logic says on date
function groupById(arr){
arr.sort((a,b)=>a.id-b.id);
let arrNew = [];
let min=0, max=0, currentid = arr[0].id;
for(i=1; i<arr.length+1;i++){
if(!arr[i] || arr[i].id!=currentid){
arrNew.push({id:currentid, start:arr[min].start, end: arr[max].end});
min = i;max=i;currentid=(arr[i]||{}).id;
}
if(!arr[i]){
break;
}
if(arr[i].start<arr[min].start){
min = i;
}
if(arr[i].end>arr[max].end){
max = i;
}
}
return arrNew;
}
var result = groupById([
{"id":1, "start":"2018-10-10", "end":"2018-11-10"},
{"id":1, "start":"2018-11-10", "end":"2018-12-10"},
{"id":2, "start":"2018-11-22", "end":"2018-11-30"}
]);
console.log(result);
you need to sort array using date comparison and also reduce it by id.
var data = [{
"id": 1,
"start": "2018-10-10",
"end": "2018-11-10"
},
{
"id": 1,
"start": "2018-11-10",
"end": "2018-12-10"
},
{
"id": 2,
"start": "2018-11-22",
"end": "2018-11-30"
}
];
let result = data.sort((a, b) => new Date(a.start) > new Date(b.start)).reduce(function(r, a) {
if (!r[a.id] || r[a.id][0].id !== a.id) {
r[a.id] = r[a.id] || [];
r[a.id].push(a);
}
return r;
}, Object.create(null));
console.log(result)
You seem to be having multiple things that need to be grouped and reduces (as shown by comment referring to other question). So I will provide some general methods in this answer.
MDN has excelent documentation on all methods used here like map, reduce and Object.value, I would advice you have a look there to understand the code better.
ES6 syntax is explained very well here
const data = [
{ id: 1, start: '2018-10-10', end: '2018-11-10' },
{ id: 1, start: '2018-11-10', end: '2018-12-10' },
{ id: 2, start: '2018-11-22', end: '2018-11-30' },
];
const groupBy = (arr, key) =>
arr.reduce(
(result, item) => (
result[item[key]].push(item), result
),
arr.reduce(
(result, item) => ((result[item[key]] = []), result),
{},
),
);
const returnLowHigh = (comp) => (a, b) =>
a.localeCompare(b) === comp ? a : b;
const lowest = returnLowHigh(-1);
const highest = returnLowHigh(1);
console.log(
Object.values(groupBy(data, 'id')).map((items) =>
items.reduce((result, { id, start, end }) => ({
id,
start: lowest(result.start, start),
end: highest(result.end, end),
})),
),
);
You can use reduce function and check the start and end date and if start date
let old = [{
"id": 1,
"start": "2018-10-10",
"end": "2018-11-10"
},
{
"id": 1,
"start": "2018-11-10",
"end": "2018-12-10"
},
{
"id": 2,
"start": "2018-11-22",
"end": "2018-11-30"
}
];
let k = old.reduce(function(acc, curr) {
let findId = acc.findIndex((item) => {
return item.id === curr.id
});
if (findId === -1) {
acc.push(curr)
} else {
let oldStartDate = createDate(acc[findId].start);
let newStartDate = createDate(curr.start);
let oldEndDate = createDate(acc[findId].end);
let newEndDate = createDate(curr.end);
if (newStartDate < oldStartDate) {
acc[findId].start = curr.start
}
if (newEndDate > oldEndDate) {
acc[findId].end = curr.end
}
}
return acc;
}, []);
console.log(k);
function createDate(dte) {
let dt = new Date(dte);
return `${dt.getYear()}-${dt.getMonth()}-${dt.getDate()}`
}

Sorting by date with underscore.js or just plain JS

I have an array of objects that have a 'date' string property.
ie:
[
{
id: 1,
startDate: '2011-4-22'
},
{
id: 2,
startDate: '2012-3-15'
},
{
id: 3,
startDate: '2011-4-22'
},
{
id: 4,
startDate: '2012-2-10'
}
]
I just want to convert the date strings to a date and sort them by startDate DESC. Can someone please tell me how to do this with teh underscore.js _sortBy method or even just plain javascript will do.
Thanks!
An Underscore solution could look like this:
a = [ /* ... */ ];
function to_date(o) {
var parts = o.startDate.split('-');
o.startDate = new Date(parts[0], parts[1] - 1, parts[2]);
return o;
}
function desc_start_time(o) {
return -o.startDate.getTime();
}
var b = _.chain(a)
.map(to_date)
.sortBy(desc_start_time)
.value();
You don't have to use named functions of course but the names do make the logic a bit clearer.
Demo: http://jsfiddle.net/ambiguous/qe9sZ/
In plain JavaScript you could do it like this:
for(var i = 0, parts; i < a.length; ++i) {
parts = a[i].startDate.split('-');
a[i].startDate = new Date(parts[0], parts[1] - 1, parts[2]);
}
var b = a.sort(function(a, b) {
return b.startDate - a.startDate;
});
Demo: http://jsfiddle.net/ambiguous/rPAPG/
forEach and sort should handle that for you:
var data = [
{
id: 1,
startDate: '2011-4-22'
},
{
id: 2,
startDate: '2012-3-15'
},
{
id: 3,
startDate: '2011-4-22'
},
{
id: 4,
startDate: '2012-2-10'
}
];
var i, c;
for(i = 0; c = data[i]; i++) {
var parts = c.startDate.split('-');
c.startDate = new Date(+parts[0], +parts[1] - 1, +parts[2]);
}
data.sort(function(a, b) {
return b.startDate - a.startDate;
});
Here's a demo; check your console.
I did it this way:
var sorted = _(list).sortBy(
function (item) {
return [new Date(item.effectiveDate).getTime(), item.batchId];
}), "batchId");
If you want it descending then it's the same thing but *-1
var sorted = _(list).sortBy(
function (item) {
return [new Date(item.effectiveDate).getTime()*-1, item.batchId];
}), "batchId");
In this example I am ordering by two fields, you can forget about the item.batchId.
Hope this helps someone.
If you are fetching datetime field from database then you can convert the datetime to timestamp and then sort. And then reverse the array.
const _ = require('underscore');
var object = [{title:"a", date:"2018-03-22T09:10:21.000Z"}, {title:"b", date:"2018-08-22T09:10:21.000Z"}, {title:"c", date:"2018-04-22T09:10:21.000Z"}];
withTimeStamp = _.map(object, function(val, key){
val.timestamp = new Date(val.date).getTime();
return val;
});
object = _.sortBy(object, 'timestamp');
object.reverse();
console.log(object);

Categories