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>
Related
I have the following javascript array:
[{ Year:2000, Jan:1, Feb: }, {Year:2001, Jan:-1, Feb:0.34 }]
I would like to add the total of Jan and Feb as a new property in the existing array.
Example:
[{ Year:2000, Jan:1, Feb:, Total: 1 }, {Year:2001, Jan:2, Feb:4, Total: -0.66 }]
How can I do this using JavaScript?
EDIT: Updated with decimal values
Assuming the empty value in Feb means 0 the following code will work.
var data = [{ Year:2000, Jan:1, Feb:0 }, {Year:2001, Jan:2, Feb:4 }];
data.forEach(item => {
item.Total = item.Jan + item.Feb;
});
console.log(data); /* [
{ Year: 2000, Jan: 1, Feb: 0, Total: 1 },
{ Year: 2001, Jan: 2, Feb: 4, Total: 6 }
]*/
Given:
const myArray = [{ Year:2000, Jan:1, Feb: 2}, {Year:2001, Jan:2, Feb:4 }];
Just iterate over the months.
myArray.forEach(y => y.Total = y.Jan + y.Feb)
You can add more months to it
myArray.forEach(y => y.Total = y.Jan + y.Feb + y.Mar + y.Apr)
Suppose data to have the array.
const data = [{ Year:2000, Jan:1, Feb: }, {Year:2001, Jan:2, Feb:4 }]
You can use Array.forEach to change the existing array.
data.forEach((item) => {
const total = (item.Jan || 0) + (item.Feb || 0);
item.Total = total;
});
here (item.Jan || 0) would ensure 0 for an undefined Jan
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);
I would like to sort the string dates to chronological order, experts inputs please?
Let say have an array object like
data = [
{id: "1", date: "18.08.2018"}
{id: "2", date: "05.01.2014"}
{id: "3", date: "01.01.2014"}
{id: "4", date: "20.05.2016"}
]
Expected is ,
when I click on the button, data should return chronological order like
data = [
{id: "3", date: "01.01.2014"}
{id: "2", date: "05.01.2014"}
{id: "4", date: "20.05.2016"}
{id: "1", date: "18.08.2018"}
]
I tried this, but it is sorting only based dates and months but not considering the year
data.sort(function(a,b){return b.date - a.date});
You can sort each by converting them into date and comparing them:
let data = [{
id: "1",
date: "18.08.2018"
},
{
id: "2",
date: "05.01.2014"
},
{
id: "3",
date: "01.01.2014"
},
{
id: "4",
date: "20.05.2016"
},
]
//asc
console.log(data.sort((a, b) => {
var d1 = a.date.split('.'),
d2 = b.date.split('.');
return new Date(d1[2], d1[1] - 1, d1[0]) - new Date(d2[2], d2[1] - 1, d2[0]);
//--------------^Year--^Month-----^day
}));
//desc
console.log(data.sort((a, b) => {
var d1 = a.date.split('.'),
d2 = b.date.split('.');
return new Date(d2[2], d2[1] - 1, d2[0]) - new Date(d1[2], d1[1] - 1, d1[0]);
}));
Using basic string compare
compare per char under ascii
data.sort(function(a,b){return b.date - a.date});
not work becuz
b.date-a.date
is minus from day then month then year
means "30-10-2010" - "19-10-2019" is bigger than 0
'3'-'1' >0 return 1
means "30-10-2010" - "31-10-2019" is smaller than 0
'3'-'3' =0 return '0'>'1' ---->return -1
a.date b.date is string using - not work in some times because somewhere not
cast string to ascii will get Nan
let data = [{
id: "1",
date: "18.08.2018"
},
{
id: "2",
date: "05.01.2014"
},
{
id: "3",
date: "01.01.2014"
},
{
id: "4",
date: "20.05.2016"
},
]
var Rs=data.sort((a, b) => {
var d1 = a.date.split('.');
var d2 = b.date.split('.');
return (d1[2]+d1[1]+d1[0]).localeCompare(d2[2]+d2[1]+d2[0]);
});
//Using basic string compare
//compare per char under ascii
var S1="Apple";
var S2="Apzle";
var S3="Apzle";
var S4="Apzle ";
console.log(S1.localeCompare(S2));
console.log(S2.localeCompare(S1));
console.log(S3.localeCompare(S3));
console.log(S3.localeCompare(S4));
console.log(Rs);
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.
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>');