Sort array of objects with date field by date - javascript

Given the following array of objects, I need to ascending sort them by the date field.
var myArray = [
{
name: "Joe Blow",
date: "Mon Oct 31 2016 00:00:00 GMT-0700 (PDT)"
},
{
name: "Sam Snead",
date: "Sun Oct 30 2016 00:00:00 GMT-0700 (PDT)"
},
{
name: "John Smith",
date: "Sat Oct 29 2016 00:00:00 GMT-0700 (PDT)"
}
];
In the above example, the final result would be John Smith, Sam Snead, and Joe Blow.
I am trying to use lodash's _.sortBy(), but I can't get any sorting to take place no matter how I try to use it:
_.sortBy(myArray, function(dateObj) {
return dateObj.date;
});
or
_.sortBy(myArray, 'date');
What do I need to change to get my array sorted properly? I also have Moment.js, so I can use it to format the date string if needed. I tried converting the date property using .unix(), but that didn't make a difference.
Thanks.

You don't really need lodash. You can use JavaScript's Array.prototype.sort method.
You'll need to create Date objects from your date strings before you can compare them.
var myArray = [{
name: "Joe Blow",
date: "Mon Oct 31 2016 00:00:00 GMT-0700 (PDT)"
}, {
name: "Sam Snead",
date: "Sun Oct 30 2016 00:00:00 GMT-0700 (PDT)"
}, {
name: "John Smith",
date: "Sat Oct 29 2016 00:00:00 GMT-0700 (PDT)"
}];
myArray.sort(function compare(a, b) {
var dateA = new Date(a.date);
var dateB = new Date(b.date);
return dateA - dateB;
});
console.log(myArray);

Here's a solution using standard Javascript by converting both values to date object and comparing their value.
myArray.sort((d1, d2) => new Date(d1.date).getTime() - new Date(d2.date).getTime());
A complete snippet:
var myArray = [
{
name: "Joe Blow",
date: "Mon Oct 31 2016 00:00:00 GMT-0700 (PDT)"
},
{
name: "Sam Snead",
date: "Sun Oct 30 2016 00:00:00 GMT-0700 (PDT)"
},
{
name: "John Smith",
date: "Sat Oct 29 2016 00:00:00 GMT-0700 (PDT)"
}
];
myArray.sort((d1, d2) => new Date(d1.date).getTime() - new Date(d2.date).getTime());
console.log(myArray);

Your date values are strings, so you need to use the new Date() constructor to change them to javascript date objects. This way you can sort them (using _.sortBy).
var myArray = [
{
name: "Joe Blow",
date: "Mon Oct 31 2016 00:00:00 GMT-0700 (PDT)"
},
{
name: "Sam Snead",
date: "Sun Oct 30 2016 00:00:00 GMT-0700 (PDT)"
},
{
name: "John Smith",
date: "Sat Oct 29 2016 00:00:00 GMT-0700 (PDT)"
}
];
myArray = _.sortBy(myArray, function(dateObj) {
return new Date(dateObj.date);
});
console.log(myArray)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.2/lodash.min.js"></script>

If you are trying to use lodash to sort dates in ascending or descending order for your array of objects, you should use _.orderBy instead of _.sortBy
https://lodash.com/docs/4.17.15#orderBy, this method allows specifying sort orders either by 'asc' or 'desc'
An example would be:
const sortedArray = _(myArray.orderBy([
function(object) {
return new Date(object.date);
}],["desc"])

A cleaner way using Lodash orderBy:
import _ from 'lodash'
const sortedArray = _.orderBy(myArray, [(obj) => new Date(obj.date)], ['asc'])

just write _.sortBy({yourCollection}, {the field name});
lodash will automatically figure that this is a date and it'll work like a magic!
Awesome!

Inspired by others answers and noticing that you used moment.js and lodash, you can combine both with _.orderBy lodash method:
import moment from 'moment'
import * as _ from 'lodash'
let myArray = [
{
name: "Joe Blow",
date: "Mon Oct 31 2016 00:00:00 GMT-0700 (PDT)"
},
{
name: "Sam Snead",
date: "Sun Oct 30 2016 00:00:00 GMT-0700 (PDT)"
},
{
name: "John Smith",
date: "Sat Oct 29 2016 00:00:00 GMT-0700 (PDT)"
}
];
myArray = _.orderBy(myArray, [(item) => {
return moment(item.date).format('YYYY-MM-DD')
}], ['desc'])

this has worked for me
myArray = _.orderBy(myArray, [item => item.lastModified], ['desc']);

Related

Newest date in dictionary using Javascript

Having an array of dictionaries I'd like to obtain the most updated record. For instance, consider the following record:
var myArray = [{
itemA: {
name: "Joe Blow",
date: "Mon Jan 31 2016 00:00:00 GMT-0700 (PDT)"
},
itemB: {
name: "Sam Snead",
date: "Sun March 30 2016 00:00:00 GMT-0700 (PDT)"
},
itemC: {
name: "John Smith",
date: "Sat Apr 29 2016 00:00:00 GMT-0700 (PDT)"
}}];
which then I would need to get the most updated record first:
myArray.sort((d1, d2) => new Date(d2.date).getTime() - new Date(d1.date).getTime());
However, I am not getting the correct result. Would you know how to get it working?
Thanks :)
I have refactored your code a little bit.
first of all, your myArray contains only one object. you cannot sort an object like that. therefore I change the structure of your array.
here is a working example:
var myArray = [
{
id: 1,
name: "Sam Snead",
date: "Sun March 30 2016 00:00:00 GMT-0700 (PDT)"
},
{
id: 2,
name: "Joe Blow",
date: "Mon Jan 31 2016 00:00:00 GMT-0700 (PDT)"
},
{
id: 3,
name: "John Smith",
date: "Sat Apr 29 2016 00:00:00 GMT-0700 (PDT)"
}
];
myArray.sort((d1, d2) => new Date(d2.date).getTime() - new Date(d1.date).getTime());
console.log(myArray)
This snippet should help you:
const myArray = [
{
itemA: {
name: "Joe Blow",
date: "Mon Jan 31 2016 00:00:00 GMT-0700 (PDT)"
},
itemC: {
name: "John Smith",
date: "Sat Apr 29 2016 00:00:00 GMT-0700 (PDT)"
},
itemB: {
name: "Sam Snead",
date: "Sun March 30 2016 00:00:00 GMT-0700 (PDT)"
}
}
];
const sortedArray = myArray.map(entry => {
let tmpObj = {};
let tmpSorted = Object.keys(entry).sort((prev, next) => {
return Date.parse(entry[next]?.date) - Date.parse(entry[prev]?.date);
});
tmpSorted.forEach(e => {
tmpObj[e] = entry[e];
})
return tmpObj;
});
console.log(sortedArray);
Your current approach attempts to sort an array of length 1, and the item in the array is an object that represents each record as a property (another object). This makes it a bit inefficient to iterate over your data. Do you need the ItemA labeling for each record and, if so, why not make this part of the object describing the record itself along with date and name?
You can look at Object.entries(), Object.keys(), or Object.values() for ways to make use of object data in an array in order to sort or map over your data.
Assuming your current schema and assuming you had an array with many such objects, if you wanted to find the most recent record in the list for some item i in your array, here is a solution.
You can create a sorted array of objects representing each record for the array item in question. Once sorted, you can take the first or last record to get the oldest or newest. See example below.
const myArray = [
{
itemA: {
name: "Joe Blow",
date: "Mon Jan 31 2016 00:00:00 GMT-0700 (PDT)"
},
itemC: {
name: "John Smith",
date: "Sat Apr 29 2016 00:00:00 GMT-0700 (PDT)"
},
itemB: {
name: "Sam Snead",
date: "Sun March 30 2016 00:00:00 GMT-0700 (PDT)"
}
}
];
const sortedEntries = myArray.map((entry) => {
const records = Object.values(entry);
const sortedRecords = records.sort(function (prev, next) {
return new Date(prev.date) < new Date(next.date) ? -1 : 1;
});
return sortedRecords;
});
// the array with records for each entry now sorted as an array from oldest to most recent date
console.log("sorted entries: ", sortedEntries);
// for example purposes, accessing your array by index, assuming you had more than 1 item in the array
const i = 0;
// get the record with most recent date for this array item
console.log(
"most recent record for example entry is: ",
sortedEntries[i][sortedEntries[i].length - 1]
);
https://codesandbox.io/s/festive-shadow-s36cf3
If you want to find the record with the latest date, you can iterate through the collection and store a record if it satisfies the comparator.
const
identityFn = (x) => x,
findBy = (collection, accessor, comparator) => {
let result, prev, record, i;
if (collection && collection.length) {
for (i = 0; i < collection.length; i++) {
record = collection[i];
if (!prev || comparator(accessor(record), prev)) {
result = record;
prev = accessor(record);
}
};
}
return result;
},
findMax = (collection, accessor = identityFn) =>
findBy(collection, accessor, (curr, prev) => curr > prev);
const
myArray = [{
itemA: { name: "Joe Blow" , date: "Mon Jan 31 2016 00:00:00 GMT-0700 (PDT)" },
itemB: { name: "Sam Snead" , date: "Sun Mar 30 2016 00:00:00 GMT-0700 (PDT)" },
itemC: { name: "John Smith" , date: "Sat Apr 29 2016 00:00:00 GMT-0700 (PDT)" },
itemD: { name: "Jane Doe" , date: null },
itemE: null,
}],
extractDate = (data) => data ? new Date(data?.date) : null,
[key, record] = findMax(
Object.entries(myArray[0]), // Access the entries (key/val pairs)
([key, val]) => extractDate(val) // Map the parsed date
);
console.log(record); // Data for key "itemC"
.as-console-wrapper { top: 0; max-height: 100% !important; }
If you want to sort the records, you can modify the code above like so:
const sortBy = (collection, accessor, comparator) =>
collection.sort((a, b) => comparator(accessor(a), accessor(b)));
const
myArray = [{
itemA: { name: "Joe Blow" , date: "Mon Jan 31 2016 00:00:00 GMT-0700 (PDT)" },
itemB: { name: "Sam Snead" , date: "Sun Mar 30 2016 00:00:00 GMT-0700 (PDT)" },
itemC: { name: "John Smith" , date: "Sat Apr 29 2016 00:00:00 GMT-0700 (PDT)" },
itemD: { name: "Jane Doe" , date: null },
itemE: null,
}],
extractDate = (data) => data ? new Date(data?.date) : null,
sorted = sortBy(
Object.entries(myArray[0]), // Access the entries (key/val pairs)
([key, val]) => extractDate(val), // Map the parsed date
(curr, prev) => prev - curr // Same as (DESC): (curr - prev) * -1
);
console.log(sorted);
.as-console-wrapper { top: 0; max-height: 100% !important; }
Edit
Added null values
Changed ([key, { date }]) => new Date(date) to ([key, val]) => extractDate(val) to handle null values

AlaSQL & ORDER BY clause with dates

I need to get my data ordered by date and events but I'm struggling to get it working using AlaSQL query on Date objects:
function testOrderBy() {
var data = [{event:'A', date: new Date('2021-04-21')},
{event:'B', date: new Date('2021-04-21')},
{event:'C', date: new Date('2021-04-21')},
{event:'D', date: new Date('2021-04-20')}];
console.log(data);
var res = alasql(`SELECT event, date FROM ? ORDER BY date, event`, [data]);
console.log(res);
}
And the result obtained is:
[ { event: 'D',
date: Tue Apr 20 2021 02:00:00 GMT+0200 (Central European Summer Time) },
{ event: 'C',
date: Wed Apr 21 2021 02:00:00 GMT+0200 (Central European Summer Time) },
{ event: 'B',
date: Wed Apr 21 2021 02:00:00 GMT+0200 (Central European Summer Time) },
{ event: 'A',
date: Wed Apr 21 2021 02:00:00 GMT+0200 (Central European Summer Time) } ]
I was expecting:
[ { event: 'D',
date: Tue Apr 20 2021 02:00:00 GMT+0200 (Central European Summer Time) },
{ event: 'A',
date: Wed Apr 21 2021 02:00:00 GMT+0200 (Central European Summer Time) },
{ event: 'B',
date: Wed Apr 21 2021 02:00:00 GMT+0200 (Central European Summer Time) },
{ event: 'C',
date: Wed Apr 21 2021 02:00:00 GMT+0200 (Central European Summer Time) } ]
The problem does not occur if dates are not Date objects but ISO strings:
function testOrderBy() {
var data = [{event:'A', date: '2021-04-21'},
{event:'B', date: '2021-04-21'},
{event:'C', date: '2021-04-21'},
{event:'D', date: '2021-04-20'}];
console.log(data);
var res = alasql(`SELECT event, date FROM ? ORDER BY date, event`, [data]);
console.log(res);
}
The result is as expected D, A, B, C
Any idea ?
It is necessary to create the table schema to correctly consider the column as a Date type, as follows:
alasql("CREATE TABLE events (event string, date date)");
alasql.tables.events.data = [{event:'A', date: new Date('2021-04-21')},
{event:'B', date: new Date('2021-04-21')},
{event:'C', date: new Date('2021-04-21')},
{event:'D', date: new Date('2021-04-20')}];
alasql(`SELECT event, date INTO HTML("#res") FROM events ORDER BY date, event`);
<script src="https://cdn.jsdelivr.net/npm/alasql#1.7.3/dist/alasql.min.js"></script>
<div id="res">
</div>

Group json data array by month

I have been trying to take the json data outputted from the database and create an new array that groups the data by month and year.
The problem is my new array doesn't output in the format that i need so i need to add the month and year but can't get the month grouping to work first. I think that might be right and resolve my issue, but I need help as arrays are confusing.
I have a codepen demo https://codepen.io/james182/pen/yLaqybP
var data = [
{ name: "First", timestampSent: "Wed, 25 Nov 2020 - 11:01 AM" },
{ name: "Second", timestampSent: "Wed, 25 Nov 2020 - 11:21 AM" },
{ name: "Third", timestampSent: "Thu, 26 Nov 2020 - 10:21 AM" },
{ name: "Fourth", timestampSent: "Fri, 27 Nov 2020 - 13:52 PM" },
{ name: "Fifth", timestampSent: "Tue, 24 Dec 2020 - 11:01 AM" },
{ name: "Sixth", timestampSent: "Wed, 25 Dec 2020 - 01:01 AM" }
];
// Clear console before running
console.clear();
var list = [];
for (i = 0; i < data.length; i++) {
var dates = data[i].timestampSent.slice(5, 16);
var mth = data[i].timestampSent.split(" ")[2];
if (!list[mth]) {
list[mth] = [];
}
list[mth].push({ name: data[i].name, date: data[i].timestampSent });
console.log(mth);
}
console.log(JSON.stringify(list));
//console.log('list', list);
/*
outcome:
[{
'Nov': [
{
'name': 'First',
'timestampSent': 'Wed, 25 Nov 2020 - 11:01 AM'
},
{
'name': 'Second',
'timestampSent': 'Wed, 25 Nov 2020 - 11:21 AM'
},
{
'name': 'Third',
'timestampSent': 'Thu, 26 Nov 2020 - 10:21 AM'
},
{
'name': 'Fourth',
'timestampSent': 'Fri, 27 Nov 2020 - 13:52 PM'
}
],
'Dec': [
{
'name': 'Fifth',
'timestampSent': 'Tue, 24 Dec 2020 - 11:01 AM'
},
{
'name': 'Sixth',
'timestampSent': 'Wed, 25 Dec 2020 - 01:01 AM'
}
]
}]
*/
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Your list should not be an array. Since you are using the month name ('Nov', 'Dec') as keys you should use an object.
var list = {};
try change the line
list = []
to
list = {}
You will be using an object, not a list.
Try it here

Reduce Object array with same values

I have an array of Javascript Objects, which contain two important values date and status
I want to reduce them into an array of unique dates, where all the values are parsed into an object, that contains each of the status and the date.
I have tried using the reduce funtion, but i can't seem to collect the dates properly, so that all the data is collected properly.
The unique statuses are: "System Labelled", "Discarded", "Saved", "Reviewed"
So for example:
[{status: "System Labelled", date: Thu Mar 05 2020 01:00:00 GMT+0100 (Central European Standard Time)},
{status: "System Labelled", date: Thu Mar 05 2020 01:00:00 GMT+0100 (Central European Standard Time)},
{status: "System Labelled", date: Thu Mar 05 2020 02:00:00 GMT+0200 (Central European Summer Time)},
{status: "Discarded", date: Tue Mar 10 2020 01:00:00 GMT+0100 (Central European Standard Time)},
{status: "Saved", date: Sat Jan 11 2020 01:00:00 GMT+0100 (Central European Standard Time)}]
---->
[{System Labelled: 1, Discarded: 0, Saved: 0, Reviwed: 0, date: Thu Mar 05 2020 01:00:00 GMT+0100 (Central European Standard Time)},
{System Labelled: 2, Discarded: 0, Saved: 0, Reviwed: 0, date: Sun Apr 05 2020 02:00:00 GMT+0200 (Central European Summer Time)},
{System Labelled: 0, Discarded: 1, Saved: 0, Reviwed: 0, date: Tue Mar 10 2020 01:00:00 GMT+0100 (Central European Standard Time)},
{System Labelled: 0, Discarded: 0, Saved: 1, Reviwed: 0, date: Sat Jan 11 2020 01:00:00 GMT+0100 (Central European Standard Time)},
Here is the code i have so far:
const objects= [{status: "System Labelled", date: new Date("19-03-2020")},
{status: "System Labelled", date: new Date("19-03-2020")},
{status: "Discarded", date: new Date("19-03-2020")},
{status: "Saved", date: new Date("19-03-2020")}]
objects.reduce((acc, curr) =>{
console.log(acc.includes(curr.status))
if(acc.includes(curr.status)){
curr[acc.status]++
}
},[])
You can take an object for grouping and get the values as result array.
You need valid dates for it.
const
statuses = ["System Labelled", "Discarded", "Saved", "Reviewed"],
objects = [{ status: "System Labelled", date: "2020-03-19" }, { status: "System Labelled", date: "2020-03-19" }, { status: "Discarded", date: "2020-03-19" }, { status: "Saved", date: "2020-03-19" }],
result = Object.values(objects.reduce((acc, { status, date }) => {
acc[date] = acc[date] || { date, ...Object.fromEntries(statuses.map(k => [k, 0])) };
acc[date][status]++;
return acc;
}, {}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
what if you get the timestamp of each date and then add it as a string after the status it has to create one single string.
Example Object
[{status: "System Labelled", date: Thu Mar 05 2020 01:00:00 GMT+0100 (Central European Standard Time)}
Would Be
"System Labelled 1583366400"
And then you could just substring the timestamp to convert it to a date again

Retrieve value from object based on sub-value?

Say I have an object like so...
object = {
thing0:{
date: 'Sat, 05 May 2012 01:00:00 EST'
},
thing1:{
date: 'Sat, 05 May 2012 12:00:00 EST'
},
thing2:{
date: 'Fri, 04 May 2012 06:00:00 EST'
}
}
I want to retrieve the thing that has the latest date. How could I create a function to quickly compare and retrieve this value?
There's no shortcut, you'd need to do a for..in loop to loop over the property names, and check each thing's date.
How about:
obj = {
thing0:{
date: 'Sat, 05 May 2012 01:00:00 EST'
},
thing1:{
date: 'Sat, 05 May 2012 12:00:00 EST'
},
thing2:{
date: 'Fri, 04 May 2012 06:00:00 EST'
}
}
key_with_max_date = Object.keys(obj).map(function(k) {
return [new Date(obj[k].date), k];
}).sort().pop()[1]
Docs: Object.keys, Array.map

Categories

Resources