Get only the last Date from an Array - javascript

I have an array of objects having a DateTime, like this:
[{Date1, Count1}, {Date2, Count2}, ...]
The Dates in the array are given by Hour (Date2 = Date1 + 1H), so I am interested in taking only the Date's last hour count.
{Date: 2020-03-21T20:00:00Z, Count: 3}
{Date: 2020-03-21T22:00:00Z, Count: 4}
{Date: 2020-03-21T23:00:00Z, Count: 15}
{Date: 2020-03-22T00:00:00Z, Count: 66}
{Date: 2020-03-22T01:00:00Z, Count: 70}
How can I reduce this Array to take in consideration only the last item of each day?
{Date: 2020-03-21T23:00:00Z, Count: 15}
{Date: 2020-03-22T01:00:00Z, Count: 70}
Something like myArray.groupBy(Date).TakeLast()...

Here's some code that only works if the dates are sorted (if they're not you can just sort via dates.sort((a, b) => a.Date.getTime() - b.Date.getTime()):
var dates = [
{ Date: new Date("2020-03-21T20:00:00Z"), Count: 3 },
{ Date: new Date("2020-03-21T22:00:00Z"), Count: 4 },
{ Date: new Date("2020-03-21T23:00:00Z"), Count: 15 },
{ Date: new Date("2020-03-22T00:00:00Z"), Count: 66 },
{ Date: new Date("2020-03-22T01:00:00Z"), Count: 70 }
];
var lastPerDay = [];
// just need to set to a value that's impossible to get normally
var prevDate = null;
// go backwards through the array to find the last instance
for (var i = dates.length - 1; i >= 0; i--) {
// need some way of combining year, month, and date into a value
var curDate = [dates[i].Date.getUTCFullYear(), dates[i].Date.getUTCMonth(), dates[i].Date.getUTCDate()].join(",");
// we haven't seen the date before
if (curDate !== prevDate) {
// add the day to the front
lastPerDay.unshift(dates[i]);
// update the previous date
prevDate = curDate;
}
}
console.log(lastPerDay);

With this, there is no need for the dates to be sorted.
let lastsHour = {}, array = [
{ date: new Date("2020-03-21T20:00:00Z"), count: 3 },
{ date: new Date("2020-03-21T22:00:00Z"), count: 4 },
{ date: new Date("2020-03-21T23:00:00Z"), count: 15 },
{ date: new Date("2020-03-22T00:00:00Z"), count: 66 },
{ date: new Date("2020-03-22T01:00:00Z"), count: 70 }
];
array.map(function (e) {
let currentDate = ""+e.date.getUTCDate()+e.date.getUTCMonth()+e.date.getUTCFullYear();
if (! lastsHour[currentDate]) {
lastsHour[currentDate] = e;
} else if (lastsHour[currentDate].date < e.date) {
lastsHour[currentDate] = e;
}
});
let result = [];
for (let key in lastsHour ) {
if (lastsHour.hasOwnProperty(key)) {
result.push(lastsHour[key]);
}
}
console.log(result);

We can use reduce method and decide on each iteration whether it is a next hour of current day. Then we can delete an array element which contains previous hour. We have O(N) by using reduce method:
const oneHourInMilliseconds = 3600000;
const result = arr.reduce((a, {Date: date, Count}) => {
let [y, m, d] = date.split(/\D+/);
let key = new Date(date).getTime();
a[key] = a[key] || { Date: date, Count };
if (a[key - oneHourInMilliseconds]) {
let [yPrev, mPrev, dPrev] = a[key - oneHourInMilliseconds].Date.split(/\D+/);
if (d == dPrev)
delete a[key-oneHourInMilliseconds];
}
return a;
},{})
console.log(Object.values(result));
An example:
let arr = [
{Date : '2020-03-21T22:00:00Z', Count: 4},
{Date : '2020-03-21T23:00:00Z', Count: 15},
{Date : '2020-03-22T00:00:00Z', Count: 66},
{Date : '2020-03-22T01:00:00Z', Count: 70},
];
const oneHourInMilliseconds = 3600000;
const result = arr.reduce((a, {Date: date, Count}) => {
let [y, m, d] = date.split(/\D+/);
let key = new Date(date).getTime();
a[key] = a[key] || { Date: date, Count };
if (a[key - oneHourInMilliseconds]) {
let [yPrev, mPrev, dPrev] = a[key - oneHourInMilliseconds].Date.split(/\D+/);
if (d == dPrev)
delete a[key-oneHourInMilliseconds];
}
return a;
},{})
console.log(Object.values(result));

var items = [
{ Date: new Date("2020-03-21T20:00:00Z"), Count: 3 },
{ Date: new Date("2020-03-21T22:00:00Z"), Count: 4 },
{ Date: new Date("2020-03-21T23:00:00Z"), Count: 15 },
{ Date: new Date("2020-03-22T00:00:00Z"), Count: 66 },
{ Date: new Date("2020-03-22T01:00:00Z"), Count: 70 },
{ Date: new Date("2020-03-22T20:00:00Z"), Count: 170 }
];
var filtered = items.filter((e, i, arr) => {
return (i == arr.length - 1 ||
arr[i].Date.toDateString() != arr[i + 1].Date.toDateString());
});
console.log(filtered);

Related

Find missing months in js array

I have the following Array
[
{ Month: '2021-05', Count: 36 },
{ Month: '2021-06', Count: 1048 },
{ Month: '2021-07', Count: 572 },
{ Month: '2021-09', Count: 3 },
{ Month: '2021-12', Count: 52 },
{ Month: '2022-01', Count: 4 },
{ Month: '2022-02', Count: 273 },
{ Month: '2022-04', Count: 96 }
]
where I am missing a few months. I know how many months is needed (could be 12 or could be more or less) and I need the missing months (like 2021-08 in this case) to be added with a count of 0. How to go about it?
Here's a pure, functional approach which will create a new array with new items, inserting all of the missing months in order. The code includes some comments explaining the procedure:
const parseDate = str => str.split('-').map(Number);
const formatDate = (year, month) => `${year}-${String(month).padStart(2, '0')}`;
function createContinuousMonthCounts (array) {
const all = [];
// get initial year/month values from first item
let [year, month] = parseDate(array[0].Month);
const advanceDate = () => {
month += 1;
if (month > 12) {
year += 1;
month = 1;
}
};
for (const item of array) {
const [y, m] = parseDate(item.Month);
// while the current month is not equal to the current item's month,
// create an entry for the month, append it, and advance to the next month
while (year !== y || month !== m) {
all.push({Month: formatDate(year, month), Count: 0});
advanceDate();
}
// after we're up to date, add the current item and advance the date
all.push({...item});
advanceDate();
}
return all;
}
const array = [
{ Month: '2021-05', Count: 36 },
{ Month: '2021-06', Count: 1048 },
{ Month: '2021-07', Count: 572 },
{ Month: '2021-09', Count: 3 },
{ Month: '2021-12', Count: 52 },
{ Month: '2022-01', Count: 4 },
{ Month: '2022-02', Count: 273 },
{ Month: '2022-04', Count: 96 },
];
const all = createContinuousMonthCounts(array);
for (const {Month, Count} of all) console.log(Month, Count);
Just a shot into the dark (please consider adding some Code to your question):
const months = [
{ Month: '2021-05', Count: 36 },
{ Month: '2021-06', Count: 1048 },
{ Month: '2021-07', Count: 572 },
{ Month: '2021-09', Count: 3 },
{ Month: '2021-12', Count: 52 },
{ Month: '2022-01', Count: 4 },
{ Month: '2022-02', Count: 273 },
{ Month: '2022-04', Count: 96 }
];
const neededMonths = [
"2021-01","2021-02","2021-03","2021-04","2021-05","2021-06","2021-07","2021-08","2021-09","2021-10","2021-11","2021-12"
]
const missedMonths = [];
months.map( m => {
if(neededMonths.indexOf(m.Month) == -1 ){
missedMonths.push(m.Month);
}
});
console.log(missedMonths);
You first need a method to find all the months between a range, then iterate across all the months and add the missing ones with count: 0:
const months = [
{ Month: '2021-05', Count: 36 },
{ Month: '2021-06', Count: 1048 },
{ Month: '2021-07', Count: 572 },
{ Month: '2021-09', Count: 3 },
{ Month: '2021-12', Count: 52 },
{ Month: '2022-01', Count: 4 },
{ Month: '2022-02', Count: 273 },
{ Month: '2022-04', Count: 96 }
]
const firstMonth = months.at(0).Month;
const lastMonth = months.at(-1).Month;
const [initialYear, initialMonth] = firstMonth.split('-');
const [endingYear, endingMonth] = lastMonth.split('-');
const allMonths = [];
let currentMonth = initialMonth;
let currentYear = initialYear;
while (`${currentYear}-${(''+currentMonth).padStart(2, '0')}` !== lastMonth) {
allMonths.push(`${currentYear}-${(''+currentMonth).padStart(2, '0')}`);
currentMonth++;
if (currentMonth === 13) {
currentMonth = 1;
currentYear++;
}
}
allMonths.forEach(month => {
if (!months.find(m => m.Month === month)) {
months.push({Month: month, count: 0});
}
});
console.log(months);

Javascript: How to sort array of objects by multiple fields?

I have a fairly classic case of sorting on many fields at the same time. What is difficult for me is passing data with fields for sorting from a separate array.
const arrayToSort = [
{
Country: "Cyprus",
Date: new Date(2001, 0, 1),
CreateBy: "William",
},
{
Country: "Belarus",
Date: new Date(1999, 0, 1),
CreateBy: "Yuliana",
},
{
Country: "Denmark",
Date: new Date(2019, 0, 1),
CreateBy: "Ava",
},
{
Country: "Albania",
Date: new Date(2000, 0, 1),
CreateBy: "Zachary",
}
];
const sortFields = ["Country", "CreateBy", "Date"];
const descending = [true, false, true];
const sortedArray = arrayToSort.sort((a, b) => {
return arrayToSort.forEach((field, index) => {
const isDate = !isNaN(Date.parse(a[field]));
if (isDate) {
const dateA = new Date(a[field]).getTime();
const dateB = new Date(b[field]).getTime();
if (descending[index] && dateA < dateB) {
return -1;
}
if (dateA > dateB) {
return 1;
}
return 0;
}
if (descending[index] && a[field] < b[field]) {
return -1;
}
if (a[field] > b[field]) {
return 1;
}
return 0;
})
})
console.log(sortedArray);
If I had a static number of elements, I would know what to do. What if I do not know the number of fields in the array? Should I use forEach here? When I console.log sortedArray, nothing changed.
I'd start off with a function which sorts 2 objects by a field and descending bool
const sortBy = (a,b,field,desc) => {
if(a[field]<b[field]) return desc ? 1 : -1
else if(a[field]>b[field]) return desc ? -1 : 1
else return 0;
};
You can then use this in a loop over your sortFields (and descending) arrays:
const arrayToSort = [
{
Country: "Cyprus",
Date: new Date(2001, 0, 1),
CreateBy: "William",
},
{
Country: "Belarus",
Date: new Date(1999, 0, 1),
CreateBy: "Yuliana",
},
{
Country: "Denmark",
Date: new Date(2019, 0, 1),
CreateBy: "Ava",
},
{
Country: "Albania",
Date: new Date(2000, 0, 1),
CreateBy: "Zachary",
}
];
const sortFields = ["Country", "CreateBy", "Date"];
const descending = [true, false, true];
const sortBy = (a,b,field,desc) => {
if(a[field]<b[field]) return desc ? 1 : -1
else if(a[field]>b[field]) return desc ? -1 : 1
else return 0;
};
const result = arrayToSort.sort( (a,b) => {
for(var i=0;i<sortFields.length;i++){
var res = sortBy(a,b, sortFields[i], descending[i]);
if(res != 0) return res;
}
return 0;
})
console.log(result);

convert array of objects to nested array of arrays for building chart

I have the following array
const data = [
{
date: "2018-01-01",
label: "MH",
qt: 10
},
{
date: "2018-04-01",
label: "MH",
qt: 30
},
{
date: "2018-02-01",
label: "GJ",
qt: 30
},
{
date: "2018-03-01",
label: "KL",
qt: 30
},
{
date: "2018-02-01",
label: "KL",
qt: 40
}
]
and i want my output to be
[
[date,MH,GJ,KL],
['Jan 2018',10,null,null],
['Feb 2018',null,30,40],
['Mar 2018',null,null,30],
['Apr 2018',30,null,null]
]
How can i achieve that in an optimize way?
And the date should be sorted in order to.
I tried doing
data.sort(function compare(a, b) {
var dateA = new Date(a.date);
var dateB = new Date(b.date);
return dateA - dateB;
});
let labelArr = data.map(l => l.label);
let dateArr = data.map(l => l.date);
labelArr = _.uniq(labelArr);
dateArr = _.uniq(dateArr);
console.log(labelArr, dateArr);
const outputArr = [];
dateArr.forEach(d => {
labelArr.forEach(l => {
const tempObj = data.filter(r => {
if (d == r.date && l == r.label) {
return r;
}
else {
return { date: d, label: l, qt: null }
}
})
outputArr.push(tempObj);
});
});
but i'm stuck here. What I was thinking is first i'll create the objects for date and label which are not present and add thier qt to null. After that i'll group by date and then insert only the qt to the result
You could take an object for keeping the array for each date and one for keeping track of the indices of the columns.
At the end set all elements to null for not set items.
var data = [{ date: "2018-01-01", label: "MH", qt: 10 }, { date: "2018-04-01", label: "MH", qt: 30 }, { date: "2018-02-01", label: "GJ", qt: 30 }, { date: "2018-03-01", label: "KL", qt: 30 }, { date: "2018-02-01", label: "KL", qt: 40 }],
cols = {},
rows = {},
result = data
.sort(({ date: a }, { date: b }) => a > b || -(a < b))
.reduce((r, { date, label, qt }) => {
date = date.slice(0, 7);
if (!rows[date]) r.push(rows[date] = [date]);
if (!cols[label]) cols[label] = r[0].push(label) - 1;
rows[date][cols[label]] = (rows[date][cols[label]] || 0) + qt;
return r;
}, [['date']])
.map((a, _, [{ length }]) => Array.from({ length }, (_, i) => a[i] || null));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I don't know if it's the best solution, but it's a working solution:
const data = [{
date: "2018-01-01",
label: "MH",
qt: 10
},
{
date: "2018-04-01",
label: "MH",
qt: 30
},
{
date: "2018-02-01",
label: "GJ",
qt: 30
},
{
date: "2018-03-01",
label: "KL",
qt: 30
},
{
date: "2018-02-01",
label: "KL",
qt: 40
}
]
function formatData(data) {
const labels = {};
const map = {};
data
.sort((a, b) => a.date < b.date ? -1 : 1)
.forEach(item => {
const date = moment(item.date).format('MMM YYYY');
labels[item.label] = true;
if (map[date]) {
map[date][item.label] = item.qt;
} else {
map[date] = { [item.label]: item.qt };
}
});
const labelsArr = Object.keys(labels);
const formattedData = Object.keys(map).map(date => {
const values = labelsArr.map(label => map[date][label] || null);
return [date, ...values];
});
return [['date', ...labelsArr], ...formattedData];
}
const result = formatData(data);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://momentjs.com/downloads/moment.min.js"></script>
I tried doing this:
data.sort(function compare(a, b) {
var dateA = new Date(a.date);
var dateB = new Date(b.date);
return dateA - dateB;
});
let labelArr = data.map(l => l.label);
let dateArr = data.map(l => l.date);
labelArr = _.uniq(labelArr);
dateArr = _.uniq(dateArr);
const outputArr = [['Date', ...labelArr]];
dateArr.forEach(d => {
const o1 = data.filter(e => e.date == d)
const o2 = o1.reduce((s, a) => {
s[a.label] = a.qt;
return s;
}, {})
const b1 = []
labelArr.forEach(l => {
b1.push(o2[l])
});
outputArr.push([moment(d).format('MMM YYYY'), ...b1]);
});
Hope it helps someone.

Get average of each day in array

I'm trying to find an efficient way in Javascript/jQuery to do the following:
I got an array with objects each containing a date and a rating. Many entries have the same date. I'd like to create another array of objects, each containing date(unique) and an average of all ratings of that day.
Example data:
var array = [{
date: "07-08-2018"
rating: 3
},{
date: "07-08-2018"
rating: 5
},{
date: "08-08-2018"
rating: 2
},{
date: "08-08-2018"
rating: 1
},{
date: "09-08-2018"
rating: 3
},{
date: "09-08-2018"
rating: 5
}
]
What I'm after:
var averageArray = [{
date: "07-08-2018",
rating: 4
},{
date: "08-08-2018",
rating: 1.5
},{
date: "09-08-2018",
rating: 4
}]
My current code (only returns 2 values instead of 3)
var oldDate = new Date(array[0].date);
var sum = 0;
var counter = 0
var newArray = []
for(var i = 0; i < array.length; i++){
var date = new Date(array[i].date);
if(date > oldDate){
console.log("new day");
var avg = (sum/counter);
//console.log(avg);
var newObject = new Object({
date: date,
rating: avg
});
newArray.push(newObject);
counter = 0;
sum = 0;
oldDate = date;
}
counter++;
sum = sum + array[i].rating;
console.log(counter + " " + sum)
}
console.log(newArray)
The best way to group things is by creating an object that uses the common values as property keys.
Then after you have them grouped in that object map the results to new array
var tmp ={};
data.forEach(function(item){
// if property for current date already exists we update existing otherwise start new one
var obj = tmp[item.date] = tmp[item.date] || {count:0, total: 0};
// increment count and total of all the ratings
obj.count ++;
obj.total += item.rating
});
var res = Object.entries(tmp).map(function(entry){
return { date: entry[0], ave: entry[1].total/entry[1].count}
})
console.log( res)
<script>
var data = [{
date: "07-08-2018",
rating: 3
},{
date: "07-08-2018",
rating: 5
},{
date: "08-08-2018",
rating: 2
},{
date: "08-08-2018",
rating: 1
},{
date: "09-08-2018",
rating: 3
},{
date: "09-08-2018",
rating: 5
}
]
</script>

Sort nested array of object in javascript

let arr = [{
name: 'Apple',
trades: [{
date: '2017.01.01',
volume: 100
}, {
date: '1995.02.01',
volume: 150
}, {
date: '2008.01.01',
volume: 250
}]
}]
Hello, I googled many documents for sorting nested object in JavaScript, but I couldn't find the way of my case and I struggled so many hours so I want to ask to how can I sort above array of objects.
What I expected result is sort array of object by trades.date like this
sortedArray = [{
name: 'Apple',
trades: [{
date: '2017.01.01',
volume: 100
}, {
date: '2008.01.01',
volume: 250
}, {
date: '1995.02.01',
volume: 150
}]
}]
How can I do this?
arr[0].trades.sort(function(a, b) {
return (new Date(b.date) - new Date(a.date));
});
You can use the array's sort method for achieving this. If you want to sort in the reverse order then just swap a and b in the return code.
Read about array.sort() and datetime in Javascript.
let arr = [{
name: 'Apple',
trades: [{
date: '2017.01.01',
volume: 100
}, {
date: '1995.02.01',
volume: 150
}, {
date: '2008.01.01',
volume: 250
}]
}]
console.log(arr[0].trades.sort((tradeA, tradeB)=>{
return (new Date(tradeA.date) - new Date(tradeB.date)) * (-1)
// or return (new Date(tradeB.date) - new Date(tradeA.date))
}))
First in your array, date needs to be a string. You can than use arrays.sort with a function which returns the result
let arr = [
{
name : 'Apple',
trades : [
{date : "2017.01.01",
volume : 100
},
{date : "1995.02.01",
volume : 150
},
{date : "2008.01.01",
volume : 250
}
]
}
]
function compare(a,b) {
var dateA = new Date(a.date);
var dateB = new Date(b.date);
if (dateA > dateB)
return -1;
if (dateA < dateB)
return 1;
return 0;
}
arr[0].trades.sort(compare);
console.log(arr);
Ensure your date format, dot is not an iso delimiter.
let toArr = (aDate) => aDate.split('.')
let toDate = ([year, month, day]) => new Date(year, month - 1, day)
let compareTrades = (a, b) => toDate(toArr(a.date)) - toDate(toArr(b.date))
let arr = [{
name: 'Apple',
trades: [{
date: '2017.01.01',
volume: 100
}, {
date: '1995.02.01',
volume: 150
}, {
date: '2008.01.01',
volume: 250
}]
}]
arr[0].trades.sort(compareTrades)
console.log(arr[0])

Categories

Resources