Iterate over two arrays of objects and create new array object - javascript

i have the following two array objects
const plan = [
{Item_ID : 01, Date:"2020-04-01", Items:"10"},
{Item_ID : 02, Date:"2020-04-01", Items:"20"},
{Item_ID : 03, Date:"2020-04-02", Items:"40"},
{Item_ID : 05, Date:"2020-04-03", Items:"10"},
];
const actual = [{Date:"2020-04-01", Actual_Items:"15"},
{Date:"2020-04-02", Actual_Items:"40"},
{Date:"2020-04-05", Actual_Items:"50"},
];
these two array shows planned sales and actual sales, based on these two array im trying to create the below array
const plan = [
{Item_ID : 01, Date:"2020-04-01", Items:"10", Actual_Items:"15"},
{Item_ID : 02, Date:"2020-04-01", Items:"20", Actual_Items:"15"},
{Item_ID : 03, Date:"2020-04-02", Items:"40", Actual_Items:"40"},
{Item_ID : 05, Date:"2020-04-03", Items:"10", Actual_Items:"0"},
{Item_ID : null, Date:"2020-04-04", Items:"0", Actual_Items:"0"},
{Item_ID : null, Date:"2020-04-05", Items:"0", Actual_Items:"50"},
];
so based on the planned array if these is actual sales done for that day we show otherwise show 0.
if nothing was planned but sales was done then Item_ID will be null Items will be null but Actual_Items need to show the number of sales.
I've created bellow map function maybe completely wrong logic and i was not able to continue future, any idea how to create this final array
let op = plan.map((e,i)=>{
let temp = actual.find(element=> element.Date === e.Date)
if(temp.Actual_Items) {
e.Actual_Items= temp.Actual_Items;
}
return e;
})
console.log(op);

You can join your two array like this:
let op = plan.concat(actual);
or
let op = [...plan, ...actual];
But before you might want to iterate over "actual" to modify the values "Item_ID" and "Items".
Solution look like:
const plan = [
{Item_ID : 01, Date:"2020-04-01", Items:"10"},
{Item_ID : 02, Date:"2020-04-01", Items:"20"},
{Item_ID : 03, Date:"2020-04-02", Items:"40"},
{Item_ID : 05, Date:"2020-04-03", Items:"10"},
];
const actual = [{Date:"2020-04-01", Actual_Items:"15"},
{Date:"2020-04-02", Actual_Items:"40"},
{Date:"2020-04-05", Actual_Items:"50"},
];
actual.forEach(function(part, index) {
part.Items = "0";
part.Item_ID = null;
this[index] = part;
}, actual);
let op = plan.concat(actual);
console.log(op);

You could use an object for same date, collect the plan values and then add actual as well.
You may finally add missing dates to the result array.
const
plan = [{ Item_ID: "01", Date: "2020-04-01", Items: "10" }, { Item_ID: "02", Date: "2020-04-01", Items: "20" }, { Item_ID: "03", Date: "2020-04-02", Items: "40" }, { Item_ID: "05", Date: "2020-04-03", Items: "10" }],
actual = [{ Date: "2020-04-01", Actual_Items: "15" }, { Date: "2020-04-02", Actual_Items: "40" }, { Date: "2020-04-05", Actual_Items: "50" }],
pattern = { Item_ID: null, Date: null, Items: "0", Actual_Items: "0" },
result = Object
.values(actual.reduce(
(r, o) => {
r[o.Date] = r[o.Date] || [{ ...pattern }];
r[o.Date].forEach(p => Object.assign(p, o));
return r;
},
plan.reduce((r, o) => {
(r[o.Date] = r[o.Date] || []).push({ ...pattern, ...o });
return r;
}, {})
))
.flat()
.reduce((r, o, i, { [i - 1]: prev }) => {
if (!r) return [o];
var p = new Date(prev.Date).getTime() + 1000 * 60 * 60 * 24;
while (p < new Date(o.Date).getTime()) {
let d = new Date;
d.setTime(p);
r.push({ ...pattern, Date: d.toISOString().slice(0, 10) });
p += 1000 * 60 * 60 * 24;
}
r.push(o);
return r;
}, undefined);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

Find pair of employees that have worked together the longest on common project

Kind of a duplicate but not at the same time because it's in python so I'm reposting it. The outputs are different as well for the same inputs.
Got a complicated assignment that I finally managed to solve but the algorithm I came up with is quite slow.
I'm pretty sure its around n^2 + n in worst case scenario.
It's supposed to go through a list of employees and return a list of employee pairs that have accumulated the most days worked together on common projectS. (the s is important)
Format for input:
EmployeeID, ProjectID, StartDate, EndDate(NULL === today)
Example input:
1,1,2019-7-4,2020-8-14
1,2,2019-12-25,2020-12-28
1,3,2018-10-12,NULL
1,4,2019-11-16,NULL
1,5,2020-1-5,2020-12-21
2,1,2018-10-3,NULL
2,2,2019-1-16,2020-3-24
2,3,2019-5-22,2019-12-26
2,4,2020-3-7,NULL
2,5,2018-1-24,2019-1-15
3,1,2019-3-21,2020-11-26
3,5,2019-9-28,2020-12-25
4,2,2018-10-22,NULL
4,3,2018-1-27,2020-8-28
5,3,2018-2-3,2020-10-14
5,5,2018-8-4,NULL
Format for output:
Employee#1, Employee#2, CommonProjectID, DaysWorked
Example output:
1,2,1,407
1,2,2,90
1,2,3,219
1,2,4,513
Here's my take on it but as I said it's quite slow and I was asked to try to optimize it. Been working on this for 5 hours now and can't come up with anything better.
export default function getHighestPair(empl) {
console.log(empl);
let pairs = {};
let daysTogether = {};
if (empl)
empl.forEach((el1) => {
/*
.slice() is used to exclude the current employee and employees before him
from the search which slightly reduces complexity. This is because
employee 5 + employee 13 is the same as employee 13 + employee 5
*/
empl.slice(empl.indexOf(el1) + 1, empl.length).forEach((el2) => {
// get start and end date of each of employee
if (el1[0] !== el2[0]) {
const startDate1 = new Date(el1[2]);
const endDate1 = el1[3] === "NULL" ? new Date() : new Date(el1[3]);
const startDate2 = new Date(el2[2]);
const endDate2 = el2[3] === "NULL" ? new Date() : new Date(el2[3]);
// check if they are in the same team (working on the same project)
if (el1[1] === el2[1]) {
if (startDate1 <= endDate2 && startDate2 <= endDate1) {
// calculate the start and end day that we need
const start = startDate1 <= startDate2 ? startDate2 : startDate1;
const end = endDate1 <= endDate2 ? endDate1 : endDate2;
if (end >= startDate2) {
// put them inside this formula and we get the time they have worked together in days
const diffTime = Math.abs(end - start);
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
const x = `${el1[0]}${el2[0]}`;
if (!daysTogether[x]) Object.assign(daysTogether, { [x]: 0 });
daysTogether[x] = 1 * daysTogether[x] + diffDays;
if (!pairs[x]) Object.assign(pairs, { [x]: [] });
pairs[x] = [...pairs[x], [el1[0], el2[0], el1[1], diffDays]];
}
}
}
}
});
});
/*
gets the index of the pair that have worked together the longest toghether from
"daysTogether" which keeps count of the days for each project
*/
return pairs[
Object.keys(daysTogether).reduce((a, b) =>
daysTogether[a] > daysTogether[b] ? a : b
)
];
}
This solution should be O(n log n).
for each record
group record by project id
for each stored record in this group, compare to current record
compare the worked together time to the longest for this group, if it's greater update longest
add current record to stored records in this group
The deObjectify cleanup is not strictly necessary
const data = [
[1,2,"2019-12-25","2020-12-28"],
[1,3,"2018-10-12",null],
[1,4,"2019-11-16",null],
[1,5,"2020-1-5","2020-12-21"],
[2,1,"2018-10-3",null],
[2,2,"2019-1-16","2020-3-24"],
[2,3,"2019-5-22","2019-12-26"],
[2,4,"2020-3-7",null],
[2,5,"2018-1-24","2019-1-15"],
[3,1,"2019-3-21","2020-11-26"],
[3,5,"2019-9-28","2020-12-25"],
[4,2,"2018-10-22",null],
[4,3,"2018-1-27","2020-8-28"],
[5,3,"2018-2-3","2020-10-14"],
[5,5,"2018-8-4",null]
];
const overlap = (e1d1, e1d2, e2d1, e2d2) => {
const startDate1 = new Date(e1d1);
const endDate1 = e1d2 === null ? new Date() : new Date(e1d2);
const startDate2 = new Date(e2d1);
const endDate2 = e2d2 === null ? new Date() : new Date(e2d2);
const start = startDate1 < startDate2 ? startDate2 : startDate1;
const end = endDate1 < endDate2 ? endDate1 : endDate2;
if (end >= start) {
const diffTime = Math.abs(end - start);
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
return diffDays;
}
return 0;
};
const result = data.reduce((acc, el) => {
let c = acc[el[1]];
if (!c) {
c = acc[el[1]] = {
overlap: 0,
e1: 0,
e2: 0,
data: []
};
};
c.data.forEach(d => {
const o = overlap(d[2], d[3], el[2], el[3]);
if (o > c.overlap) {
c.overlap = o;
c.e1 = d[0];
c.e2 = el[0];
}
});
c.data.push(el);
return acc;
}, {});
const deObjectify = Object.entries(result).map(([projectId, {e1, e2, overlap}]) => ({e1, e2, projectId, overlap}));
console.log(deObjectify);
console.log("inner workings");
console.log(result);
So this is my final code...
Result:
[ { emA: 1, emB: 2, sum: 1230, details: [{ proj: 1, days: 407 }, { proj: 2, days: 90 }, { proj: 3, days: 219 }, { proj: 4, days: 514 }]}
, { emA: 1, emB: 5, sum: 1084, details: [{ proj: 3, days: 733 }, { proj: 5, days: 351 }]}
, { emA: 1, emB: 4, sum: 1055, details: [{ proj: 2, days: 369 }, { proj: 3, days: 686 }]}
, { emA: 4, emB: 5, sum: 937, details: [{ proj: 3, days: 937 } ]}
, { emA: 1, emB: 3, sum: 758, details: [{ proj: 1, days: 407 }, { proj: 5, days: 351 }]}
, { emA: 2, emB: 4, sum: 652, details: [{ proj: 2, days: 433 }, { proj: 3, days: 219 }]}
, { emA: 2, emB: 3, sum: 616, details: [{ proj: 1, days: 616 } ]}
, { emA: 3, emB: 5, sum: 455, details: [{ proj: 5, days: 455 } ]}
, { emA: 2, emB: 5, sum: 384, details: [{ proj: 3, days: 219 }, { proj: 5, days: 165 }]}
]
const data =
[ [ 1, 1, '2019-7-4', '2020-8-14' ]
, [ 1, 2, '2019-12-25', '2020-12-28' ] // EmployeeID, ProjectID, StartDate, EndDate(null === today)
, [ 1, 3, '2018-10-12', null ]
, [ 1, 4, '2019-11-16', null ]
, [ 1, 5, '2020-1-5', '2020-12-21' ]
, [ 2, 1, '2018-10-3', null ]
, [ 2, 2, '2019-1-16', '2020-3-24' ]
, [ 2, 3, '2019-5-22', '2019-12-26' ]
, [ 2, 4, '2020-3-7', null ]
, [ 2, 5, '2018-1-24', '2019-1-15' ]
, [ 3, 1, '2019-3-21', '2020-11-26' ]
, [ 3, 5, '2019-9-28', '2020-12-25' ]
, [ 4, 2, '2018-10-22', null ]
, [ 4, 3, '2018-1-27', '2020-8-28' ]
, [ 5, 3, '2018-2-3', '2020-10-14' ]
, [ 5, 5, '2018-8-4', null ]
]
const
oneDay = 24 * 60 * 60 * 1000 // hours*minutes*seconds*milliseconds
, setDate = YMD =>
{
let [Y,M,D] = YMD.split('-').map(Number)
return new Date(Y,--M,D)
}
// group Employees by project id , change date string to JS newDate
const Proj_Emps = data.reduce( (r,[EmployeeID, ProjectID, StartDate, EndDate])=>
{
let stD = setDate(StartDate)
, enD = EndDate ? setDate(EndDate) : new Date()
r[ProjectID] = r[ProjectID] ?? []
r[ProjectID].push({EmployeeID,stD,enD})
return r
}, {})
// combination of pairs of employees per project
let combination = {}
for (let proj in Proj_Emps)
for (let i = 0; i < Proj_Emps[proj].length - 1; i++)
for (let j = i + 1; j < Proj_Emps[proj].length; j++)
{
let emA = Proj_Emps[proj][i]
let emB = Proj_Emps[proj][j]
if (( emA.enD <= emB.enD && emA.enD > emB.stD )
||( emB.enD <= emA.enD && emB.enD > emA.stD )
){
let
D1 = emA.stD > emB.stD ? emA.stD : emB.stD
, D2 = emA.enD < emB.enD ? emA.enD : emB.enD
, days = Math.ceil((D2 - D1) / oneDay)
, key = `${emA.EmployeeID}-${emB.EmployeeID}`
;
combination[key] = combination[key] ?? { emA: emA.EmployeeID, emB: emB.EmployeeID, sum:0, details:[] }
combination[key].details.push({proj: Number(proj), days })
combination[key].sum += days
}
}
let Result =
Object.entries(combination)
.sort((a,b)=> b[1].sum - a[1].sum )
.map(([k,v])=>v)
Result.forEach(el => console.log( JSON.stringify(el).replaceAll('"','')))
.as-console-wrapper { max-height: 100% !important; top: 0 }
.as-console-row::after { display: none !important; }
I also fixed a bugg (typo) on the calculation of dates

Group array by two different keys and sum values

I'm working with an array on React and i'm trying to filter it by month and year, i managed to do it by month but for some reason i can't add the year key, this is what i have so far:
This is the array that i have originally:
paid = [
{amount:155, month:11, year:2020, date:11-11-2020}
{amount:120, month:11, year:2021, date:05-11-2021}
{amount:135, month:12, year:2020, date:11-12-2020}
...
]
const group = groupBy(d, (invoices) => invoices.month); //d is the array called "paid"
This is the groupBy function:
function groupBy(list, keyGetter) {
const map = new Map();
list.forEach((item) => {
const key = keyGetter(item);
const collection = map.get(key);
if (!collection) {
map.set(key, [parseInt(item.amount)]);
} else {
collection.push(parseInt(item.amount));
}
});
return map;
}
And this is the result i have:
grouped = [
{name:11, values: [155,120...]},
{name:12, values: [135...]
];
And what i want to to is to also have it grouped by the year, so for example, the month 11, shouldn't have two values, because on the original array i have on month that's 11 from 2020 and one from 2021, so what i want to have as a result is this:
grouped = [
{name:11/2020, values: [155...]},
{name:11/2021, values: [120...]},
{name:12/2020, values: [135...]
];
Can anyone help me with this?
If I understand correctly you want to sum all values per month per year. I guess this will work.
// Generate random data.
const genData = () => {
const d = [];
for(let i =0; i < 1000; i++) {
d.push({
year: Math.round(Math.random() * 10) + 2001,
month: Math.round(Math.random() * 12),
amount: Math.round(Math.random() * 100) + 100
})
}
return d;
};
// Sum all data per month per year
const sumData = (d) => {
const summed = {};
for(const {year,month,amount} of d) {
// By using a unique key for each year/month combi you can easily access the
// intermedeiate result and add another value.
const key = year + '/' + month;
// If it does not yet exist, create an empty record for this year/month
if(!(key in summed)) {
summed[key] = {year,month, sum:0, values:[]};
}
// Grab the intermediate values and add amount to sum and to values
summed[key].values.push(amount)
summed[key].sum += amount;
}
return Object.values(summed);
};
// Run all
const d = genData();
console.log(d);
console.log(sumData(d));
Working with Array.reduce method could be more simple, for the example I added some values:
const paid = [
{amount:155, month:11, year:2020, date:'11-11-2020'},
{amount:160, month:11, year:2020, date:'11-11-2020'},
{amount:120, month:11, year:2021, date:'05-11-2021'},
{amount:130, month:11, year:2021, date:'05-11-2021'},
{amount:135, month:12, year:2020, date:'11-12-2020'},
{amount:145, month:12, year:2020, date:'11-12-2020'}
]
const grouped = paid.reduce((acc,val)=>{
if(acc[val.month+'/'+val.year]){
acc[val.month+'/'+val.year].push(val.amount)
} else {
acc[val.month+'/'+val.year] = [val.amount]
}
return acc
}, {})
console.log(JSON.stringify(grouped,null,2))
EDIT -----------------
I edited the code to use the group by function and to produce an array containing the values and the sum. you can group passing an array containing first level key (like ['month'] or ['month', 'year']):
const paid = [
{ amount: 155, month: 11, year: 2020, date: "11-11-2020" },
{ amount: 160, month: 11, year: 2020, date: "11-11-2020" },
{ amount: 120, month: 11, year: 2021, date: "05-11-2021" },
{ amount: 130, month: 11, year: 2021, date: "05-11-2021" },
{ amount: 135, month: 12, year: 2020, date: "11-12-2020" },
{ amount: 145, month: 12, year: 2020, date: "11-12-2020" }
];
const groupBy = (data, keys) => {
return Object.values(
data.reduce((acc, val) => {
const name = keys.reduce((finalName,key)=> finalName + val[key]+'/','').slice(0, -1)
if (acc[name]) {
acc[name].values.push(val.amount);
acc[name].sum += val.amount;
} else {
acc[name] = {
name,
sum:val.amount,
values:[val.amount]
};;
}
return acc;
}, {})
);
};
console.log(JSON.stringify(groupBy(paid, ['month','year']), null, 2));

Sort array with timestamps and merge similar dates

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.

Create new object based on filtered data

I need some help. I need to calculate the amount of user actions in each month of current year. I have an array of dates:
let years = ['2017', '2018', '2019']
let datesArray = [
{date: "2019-06-05", userActionsAmount: 88},
{date: "2019-06-04", userActionsAmount: 314}
]
and I have the count object
let counts = {}
then I iterate through this like that:
years.forEach(year => {
counts[year] = datesArray.filter(singleDay => singleDay.date.slice(0, -6) === year).reduce((acc, obj) => {
return acc + obj.userActionsAmount
}, 0)
})
with this code result of counts is:
{2017: 0, 2018: 0, 2019: 402} which is ok, but I need to break the date in to months, so I need something like this:
{ 2017: []},
{ 2018: []}
{ 2019: [
{ '01': 0 },
{ '02': 0 },
{ '03': 0 },
{ '04': 0 },
{ '05': 0 },
{ '06': 402 },
{ '07': 0 },
{ '08': 0 },
{ '09': 0 },
{ '10': 0 },
{ '11': 0 },
{ '12': 0 }
]}
you can do it like this:
let datesArray = [
{date: "2019-06-05", userActionsAmount: 88},
{date: "2019-06-04", userActionsAmount: 314}
]
let result={};
datesArray.forEach(dateItem=>{
let date=dateItem.date.split("-");
let year=date[0];
let month=date[1];
if(!result[year])
result[year]={};
if(!result[year][month])
result[year][month]=0;
result[year][month]+=dateItem.userActionsAmount;
})
That's basically a very simple grouping
const datesArray = [
{date: "2019-06-05", userActionsAmount: 88},
{date: "2019-06-04", userActionsAmount: 314}
];
const groupedByMonth = datesArray.reduce((a, b) => a.set(b.date.substring(0,7), ~~a.get(b.date.substring(0,7)) + b.userActionsAmount), new Map);
console.log([...groupedByMonth]);
To get it to your format, you could do something like
const yourFormat = years.map(e => ({
[e]: Array.from(groupedByMonth).filter(([k, v]) => k.substring(0,4) === e).map(([k, v]) => ({[k.substring(5,7)]: v}))
}));
then
You could create properties when needed.
Here are two solutions : one with array methods and second more explicit.
Initialization :
const monthsKeys = ["01", "02", "03","04", "05", "06", "07", "08", "09", "10", "11", "12"];
const years = ['2017', '2018', '2019'];
const datesArray = [
{date: "2019-06-05", userActionsAmount: 88},
{date: "2019-06-04", userActionsAmount: 314}
];
const counts = {};
Solution 1 :
years.forEach( y => { counts[y] = []; });
datesArray.forEach(dateCount => {
const [year, month, day] = dateCount.date.split("-");
if (counts[year].length === 0) monthsKeys.forEach(m => {counts[year].push({[m] : 0});});
counts[year][Number(month) - 1][month] += dateCount.userActionsAmount;
});
console.log(counts);
Solution 2 :
// fill counts with years
for (const y of years) {
counts[y] = [];
}
// fill counts with months and count
for (const e of datesArray) {
const splittedDate = e.date.split("-");
const year = splittedDate[0];
const month = splittedDate[1];
// create year if needed, not necessary if years array is sure
if ( ! year in counts) {
counts[year] = [];
}
// create monthes if needed
if (counts[year].length === 0) {
for (const m of monthsKeys) {
counts[year].push({[m]: 0});
}
}
// add value
counts[year][Number(month) - 1][month] += e.userActionsAmount;
}
console.log(counts)
Why an array of objects for year values (months counts) and not simply an object?
This solution has some varation from the OP's expected output, but I believe that it should fit OP's requirements. If not, it's about a step to get the output as desired...
const years = [2017, 2018, 2019]
const dates = [{
date: "2019-06-05",
userActionAmount: 88
},
{
date: "2019-06-04",
userActionAmount: 314
}
]
const transform = (years, dates) => dates.reduce(
(output, {
date,
userActionAmount,
parsedDate = new Date(date),
year = parsedDate.getFullYear(),
month = parsedDate.getMonth() + 1,
yearData = output[year]
}) =>
(yearData[month] += userActionAmount) && output,
Object.fromEntries(years.map(year => [year, Object.fromEntries(Array.from({
length: 12
}, (_, x) => [x + 1, 0]))])))
const output = transform(years, dates)
console.log(output)
// This output lets you get total amount
// of some given month in the following way:
const monthAmount = output[2019][6]
console.log (monthAmount)

JavaScript: Compare dates in an array and sum the "price" for each month/year

I have a json file with multiple transactions with a date and a price attribute. Now I want to compare the dates and if they are in the same month and year I want to sum up the prices.
JSON:
transactions: [
{
date: "2017-11-17",
price: "28",
},
{
...
}
JavaScript:
request.onload = function() {
for(const transaction of request.response.transactions) {
let year = new Date(transaction.date).getFullYear();
let month = new Date(transaction.date).getMonth();
console.log(year + ' ' + month); // output: 2017-11 ...
}
};
I tried to loop over the json object but I struggle to find a solution to compare the dates.
Edit: Edited example with Object.assign instead of Object spread.
You'll need to use reduce to sum the prices. See comments for details.
const transactions = [{
date: "2017-11-17",
price: "28",
},
{
date: "2017-12-17",
price: "23",
},
{
date: "2017-11-17",
price: "12",
},
{
date: "2017-10-17",
price: "55",
},
{
date: "2017-11-17",
price: "09",
},
];
const sumTransactions = (transactions) => {
const summed = transactions.reduce((acc, current) => {
// Get the current date object
const date = new Date(current.date);
// Create your key/identifier
const key = `${date.getFullYear()}-${date.getMonth() + 1}`;
// Retreive the previous price from the accumulator
const previousPrice = acc[key]; // Might also return undefined
// Create your temp current price value, and be sure to deal with numbers.
let currentPrice = Number(current.price);
// If you had a previous value (and not undefined)
if (previousPrice) {
// Add it to our value
currentPrice += Number(previousPrice);
}
// Return the future accumulator value
return Object.assign(acc, {
[key]: currentPrice, // new values will overwrite same old values
})
}, {})
// Once we have all values, get the dates, and sort them (default: earlier first)
// Return an array of each value from the summed object to our sortedArray
const sortedArray = Object.keys(summed).sort().map((val) => {
return summed[val];
});
console.log("sortedArray", sortedArray);
};
sumTransactions(transactions);
I experimented a bit and came up with this solution:
var transactions = [
{
date: "2017-11-17",
price: "28",
},
{
date: "2017-12-17",
price: "22",
},
{
date: "2017-12-17",
price: "20",
}
]
var sumedUpDates = [];
var prices = [];
function isDateSumedUp(date) {
return sumedUpDates.indexOf(date.substring(0, 7)) !== -1;
}
function sumUpDate(date) {
var sum = 0;
transactions.forEach(t => {
if(t.date.substring(0, 7) === date.substring(0, 7)) {
sum += parseInt(t.price);
}
});
sumedUpDates.push(date.substring(0, 7));
prices.push(sum);
}
transactions.forEach(t => {
if(!isDateSumedUp(t.date)) {
sumUpDate(t.date);
}
});
var obj = {};
sumedUpDates.forEach((d, i) => obj[d] = prices[i]);
console.log(obj);
This solutions uses map to format your dates into year/month format for each object entry and then reduce to sum them by those separated dates.
const transactions = [
{date:"2017-11-17", price: "28",},
{date:"2017-12-17", price: "28",},
{date:"2017-11-17", price: "20",},
{date:"2017-12-17", price: "2",},
{date:"2017-11-17", price: "58",},
{date:"2017-11-17", price: "8",},
{date:"2017-10-17", price: "30",},
{date:"2018-11-17", price: "1",},
];
const mapper = single => {
let d = single.date.split('-');
let p = Number(single.price);
return { year: d[0], month: d[1], price: p };
}
const reducer = (group, current) => {
let i = group.findIndex(single => (single.year == current.year && single.month == current.month));
if (i == -1) {
return [ ...group, current ];
}
group[i].price += current.price;
return group;
};
const sumPrices = transactions.map(mapper).reduce(reducer, []);
console.log(sumPrices);
var array = [];
for (var i = 0; i < transactions.length; i++) {
var date = new Date(transactions[i].date);
var ym = date.getFullYear() + "-" + date.getMonth();
if (array[ym] == null) {
array[ym] = 0;
}
array[ym] += parseInt(transactions[i].price);
}
With this data
var transactions = [{
date: "2017-11-17",
price: "28",
},
{
date: "2017-12-17",
price: "5",
},
{
date: "2016-02-17",
price: "28",
},
{
date: "2015-11-17",
price: "25",
},
{
date: "2016-02-17",
price: "12",
},
{
date: "2017-11-17",
price: "50",
}
];
This will give you the sum of all of the year-months duplicates like this :
[
2017-10: 78,
2017-11: 5,
2016-1: 40,
2015-10: 25
]
Another solution is reduce:
var transactions = [
{date: "2017-11-17",price: "28"},
{date: "2017-12-17",price: "22"},
{date: "2017-12-17",price: "20"}
];
var result = transactions.reduce(function(acc, obj) {
var key = obj.date.substr(0,7);
acc[key] = (acc[key] || 0) + +obj.price;
return acc;
}, Object.create(null));
console.log(result);

Categories