I have a sales data for a couple of years in an array:
var= ['Jan-2019',325678], ['feb-2019', 456789], ['Mar-2019',-12890],.....['Dec-2021', 987460]
1 -want to calculate the net total amount of profit/losses over the entire period.
2- Average of the (changes) in profit/losses over the entire period - track the total change in profits from month to month and find the average(total/number of months)
3 - greatest increase in profit (month and amount)over the whole period
4 - greatest decrease3in profit (month and amount)over the whole period
Tried to solve number 1 using :
`
const profitMonths = data.filter(el => el[1] > 0);
console.log("Total:", profitMonths.map(el => el[1]).reduce((a, b) => a + b));
console.log;
`
but the sum I am getting is different from what excel and calculator is giving me.
I will appreciate some help here
Not sure what is the format of your original data records for each of the months. I assumed that your data format is like below. But you could get the sum of each months growth or loss (earnings) like this and also get what you were trying as well (profit months total sales):
const data = [
['Jan-2019', 325678],
['Feb-2019', 456789],
['Mar-2019', -12890],
];
const earningsArray = data.map((el) => el[1]);
const profitMonths = data.filter((el) => el[1] > 0);
const salesOnProfitMonths = profitMonths
.map((el) => el[1])
.reduce((accVal, curVal) => accVal + curVal, 0);
const avgOfProfitAndLoss =
earningsArray.reduce((accVal, curVal) => accVal + curVal, 0) / data.length; // get the average of all total and losses
const maxMonth = {
monthName: '',
profit: 0,
};
const minMonth = {
monthName: '',
profit: 0,
};
data.forEach((month) => {
if (month[1] > maxMonth.profit) {
maxMonth.monthName = month[0];
maxMonth.profit = month[1];
}
if (month[1] < minMonth.profit) {
minMonth.monthName = month[0];
minMonth.profit = month[1];
}
return { maxMonth, minMonth };
});
console.log('Total sale of profit months: ', salesOnProfitMonths);
console.log('Total average : ', avgOfProfitAndLoss);
console.log('The month with max profit is : ', maxMonth);
console.log('The month with min profit is : ', minMonth);
Using .reduce() you can actually build an object to the returned based on all of the data from your original array.
const data = [['Jan-2019', 325678], ['feb-2019', 456789], ['Mar-2019',-12890], ['Dec-2021', 987460]]
let result = data.reduce((a, b, i) => {
let d = (i > 1) ? a : {total: a[1], average: a[1], sumChange: 0, lastMonth: a[1], increase: a, decrease: a},
change = b[1] - d.lastMonth
d.total += b[1]
d.sumChange += change
d.lastMonth = b[1]
d.average = d.sumChange / i
d.increase = (d.increase[1] > change) ? d.increase : [b[0], change]
d.decrease = (d.decrease[1] < change) ? d.decrease : [b[0], change]
return d
})
console.log(result) // Return the full object
console.log(result.total) // Only return one value, the total
Based on the array/input you provided, this should provide a net total, average profit/loss, highest increase from the previous month, and highest decrease from the previous month.
EDIT
I had to make a few adjustments after getting some clarification. But this again should return a single object that holds values for everything requested by OP. (the sumChange and lastMonth values are only there to help with the .reduce() function month over month)
NOTES
Just for clarity as OP claimed they were not getting the right values, here is a breakdown based on the provided data:
Date
Sales
Change
Jan-2019
$325,678
N/A
Feb-2019
$456,789
$131,111
Mar-2019
-$12,890
-$469,679
Dec-2021
$987,460
$1,000,350
Based on this input, calculated manually:
The "Average of the (changes) in profit/losses over the entire period" is $220,594 (($131,111 + $469,679 + $1,000,350) / 3).
The "greatest increase in profit (month and amount)over the whole period" would be Dec-2021 with a $1,000,350 increase.
And the "greatest decrease in profit (month and amount)over the whole period" would be Mar-2019 with -$469,679.
This is exactly what my .reduce() does produce, so I'm not sure what actual input or output OP is getting (or how they are applying this to their code/data).
Related
I have bunch of electricity meter readings which have irregular dates. See below :
ReadingDate Meter
19/01/2021 5270
06/03/2021 5915
11/05/2021 6792
08/07/2021 7367
9/9/2021 8095
8/11/2021 8849
02/12/2021 9065
17/01/2022 9950
Now I'd like to transform this into monthly readings, using just this data, to end up with a table like this
Month Usage
2021-01 452
2021-02 393
2021-03 416
2021-04 399
2021-05 341
2021-06 297
2021-07 347
2021-08 358
2021-09 369
2021-10 389
2021-11 295
2021-12 586
2022-01 308
Now, I have a working solution, but I'm sure there's a more beautiful concise way of doing it.
What I do is to create an intermediate array that has one line for each date between first and last meter readings.
Each item in the array has 3 values :
the date
the average value for that date (calculated by counting the days between meter readings and dividing that by change in the meter.
the corresponding month
The last step then is to loop over this intermediate array and sum the values for each different month.
Here's the working code (its taken from Google Apps Script so please ignore the spreadsheet specific stuff:
var DailyAveragesArray = [['Date','Usage','Month']];
var monthlyObject = {};
var monthlyArray = [['Month','Usage']];
function calculateAverageDailyFigures() {
// give indices for the useful columns, 0 numbered
var ReadingDateColumn = 0;
var MeterReading = 1;
// Read into an array
var MeterReadingData = ss.getDataRange().getValues() // Get array of values
const sortedReadings = MeterReadingData.slice(1).sort((a, b) => a[0] - b[0]);
// from https://flaviocopes.com/how-to-sort-array-by-date-javascript/
// First calculate the number of days and average daily figure for each row
// Note we don't do this for the last row
for(i=0; i < sortedReadings.length - 1 ; i++){
var NumberOfDays = (sortedReadings[i+1][0] - sortedReadings[i][0])/(1000*3600*24);
sortedReadings[i].push(NumberOfDays);
var MeterDifference = sortedReadings[i+1][1] - sortedReadings[i][1];
var AverageDailyFigure = MeterDifference/NumberOfDays;
sortedReadings[i].push(AverageDailyFigure);
}
BuildDailyArray(sortedReadings);
}
function BuildDailyArray(sortedReadings){
// For each row in sorted , loop from the date to the next date-1 and create columns date and Usage
for(i=0; i<sortedReadings.length -1 ;i++){
for (var d = sortedReadings[i][0]; d < sortedReadings[i+1][0]; d.setDate(d.getDate() + 1)) {
var newDate = new Date(d);
var month = newDate.getFullYear() + '-' + ('0' + (newDate.getMonth() + 1)).slice(-2);
DailyAveragesArray.push([newDate,sortedReadings[i][3],month]);
// Check if the month is in the object and add value, otherwise create object an add value
if(month in monthlyObject){
monthlyObject[month] = monthlyObject[month] + sortedReadings[i][3];
} else {
Logger.log('Didnt find month so create it');
monthlyObject[month] = sortedReadings[i][3];
}
}
}
Logger.log(DailyAveragesArray.length);
Logger.log(monthlyObject);
var DailyUsageData = ss.getRange('D1:F'+DailyAveragesArray.length);
DailyUsageData.setValues(DailyAveragesArray);
BuildMonthlyArray();
}
function BuildMonthlyArray(){
const keys = Object.keys(monthlyObject);
Logger.log(keys);
keys.forEach((key, index) => {
monthlyArray.push([key,Math.round(monthlyObject[key])]);
});
var MonthlyUsageData = ss.getRange('H1:I'+monthlyArray.length);
MonthlyUsageData.setValues(monthlyArray);
}
So, my question is, how would I do this nicer, more beautifully, not so verbose ?
I'm not sure what the correct term is for what I want to do. I don't think it's resampling .
I'd appreciate any comments.
Thanks / Colm
Here is my shot on this.
The way i'm doing it:
Initializing all days and its value
Grouping by month
Calculating the average per month
Explanation a bit more precise
initDateFromString
The method initDateFromString takes a dates with the format DD/MM/YYYY and return the associated js date object
initAllDates
The method initAllDates will split the data into day and add the average value of the difference for each day
for example, for the first two readings, it will result to an array of dates looking like :
date
value
19/01/2021
14.02
20/01/2021
14.02
....
....
05/03/2021
14.02
06/03/2021
14.02
The value 14.02 comme from the following calcul :
(newReadingMeter - oldReadingMeter)/nbDaysBetweenDates
Which in this example is (5915 - 5270)/46 = 14.02
joinToMonth
The joinToMonth method will then group the days into month with all the days value summed !
const data = [{
ReadingDate: '19/01/2021',
Meter: 5270
},
{
ReadingDate: '06/03/2021',
Meter: 5915
},
{
ReadingDate: '11/05/2021',
Meter: 6792
},
{
ReadingDate: '08/07/2021',
Meter: 7367
},
{
ReadingDate: '9/9/2021',
Meter: 8095
},
{
ReadingDate: '8/11/2021',
Meter: 8849
},
{
ReadingDate: '02/12/2021',
Meter: 9065
},
{
ReadingDate: '17/01/2022',
Meter: 9950
}
]
function initDateFromString(dateString){
let dateParts = dateString.split("/");
return new Date(+dateParts[2], dateParts[1] - 1, +dateParts[0]);
}
function initAllDates(data){
let dates = []
let currentValue = data.shift()
const currentDate = initDateFromString(currentValue.ReadingDate)
data.forEach(metric => {
const date = initDateFromString(metric.ReadingDate)
const newDates = []
while(currentDate < date){
newDates.push({date: new Date(currentDate)})
currentDate.setDate(currentDate.getDate() + 1)
}
dates = dates.concat(newDates.map(x => {
return {Usage: (metric.Meter - currentValue.Meter) / newDates.length, date: x.date}}
))
currentDate.setDate(date.getDate())
currentValue = metric
})
return dates
}
function joinToMonth(dates){
return dates.reduce((months, day) => {
const month = day.date.getMonth()
const year = day.date.getFullYear()
const existingObject = months.find(x => x.month === month && x.year === year)
if (existingObject) {
existingObject.total += day.Usage
} else {
months.push({
month: day.date.getMonth(),
year: day.date.getFullYear(),
total: day.Usage,
})
}
return months;
}, []);
}
const dates = initAllDates(data)
const joinedData = joinToMonth(dates)
console.log(joinedData)
I am pretty new to javascript and working on problems from leetcode.
The description included is:
"Given an array of unique integers salary where salary[i] is the salary of the employee i.
Return the average salary of employees excluding the minimum and maximum salary."
When I run my code it says that I have the incorrect output when the following array is passed in.
[25000,48000,57000,86000,33000,10000,42000,3000,54000,29000,79000,40000]
Expected Output: 41700.00000
My Output: 41000.00000
I've compared my code to other submissions and as far as I can tell mine should run the same.
Here is my code:
function average(salary) {
var sortedSalary = salary.sort();
var total = sortedSalary.reduce((curr, acc) => {
return curr + acc
}, 0);
var result = (total - sortedSalary[0] - sortedSalary[sortedSalary.length - 1]) / (sortedSalary.length - 2);
return result;
};
console.log(average([25000,48000,57000,86000,33000,10000,42000,3000,54000,29000,79000,40000]));
Thank you for any insight into this.
You're sorting the array alphabetically, not by the smallest number, so you end up removing the wrong numbers.
Use sort((a, b) => a - b) instead:
const salary = [25000,48000,57000,86000,33000,10000,42000,3000,54000,29000,79000,40000]
function average(salary) {
var sortedSalary = salary.sort((a, b) => a - b);
var total = sortedSalary.reduce((curr, acc) => { return curr + acc }, 0);
var result = (total - sortedSalary[0] - sortedSalary[sortedSalary.length - 1]) / (sortedSalary.length - 2);
return result;
};
console.log(average(salary))
const average = salary => salary
.sort((a,b) => a - b) // sort numerically
.filter((_,i,l) => i > 0 && i < l.length - 1) // remove first and last index
.reduce((a,s) => a + s) / (salary.length - 2) // calculate average
console.log(average([25000,48000,57000,86000,33000,10000,42000,3000,54000,29000,79000,40000]))
It does not need to be sorted at all. Apart from that, sorting is O(N Log N) not sufficiently efficient here. You just have to come up with an O(N) algorithm for this problem.
This'll pass:
var average = function(salary) {
if (salary.length < 3) {
return 0
}
let min = salary[0]
let max = salary[0]
let sum = 0
for (let sal of salary) {
if (sal > max) {
max = sal
}
if (sal < min) {
min = sal
}
sum += sal
}
return (sum - max - min) / (salary.length - 2)
};
References
For additional details, you can see the Discussion Board. There are plenty of accepted solutions with a variety of languages and explanations, efficient algorithms, as well as asymptotic time/space complexity analysis1, 2 in there.
For interviews:
We'd like to write bug-free and clean codes based on standards and conventions (e.g., c1, 2, c++1, 2, java1, 2, c#1, 2, python1, javascript1, go1, rust1).
For easy questions, brute force algorithms usually get accepted. For interviews, brute force is less desired, especially if the question would be an easy level, like the one you're solving.
I have an array of date ranges(selectedRanges) which shows assigned dates for a member between the main date range. I want to know the date ranges where he/she is unassigned. Please refer to the below example.
mainDateRange = ['01-01-2020', '14-06-2020'];
selectedRanges = [
['03-01-2020','04-01-2020'],
['03-01-2020','05-01-2020'], //overlapping dates
['11-01-2020','13-01-2020'],
['01-02-2020','20-02-2020'],
['15-03-2020','18-03-2020'],
['06-01-2020','06-01-2020'], //date ranges will not be ordered
['03-01-2020','04-01-2020']
]; //dates that the member has work assigned
Desired output
excludedRanges = [
['01-01-2020','02-01-2020'],
['07-01-2020','10-01-2020'],
['14-01-2020','31-01-2020'],
['21-02-2020','14-03-2020'],
['19-03-2020','14-06-2020']
]; //shows all the unassigned periods(ranges)
selectedRanges date ranges will have ranges in random order and also may have duplicate and overlapping dates.
I have searched a lot and found nothing. I am only able to get the unselected dates, not as a range. Please help.
Thank you
Interesting problem, I'll propose an approach to achieve this desired behavior by doing the following:
Transform all string dates into date objects.
Sort the selectedRanges array in ascending order using the start and end dates. This sorting step is cricual to finding the date range gaps.
Adding a "moving cursor" date that moves between the mainDateRange to find and add the missing ranges to the output array.
Before we start the date calculations, we'll need a few helper functions. I've added two functions to go back and forth between the date object and the string format you have (dd-mm-yyyy). Please note that you may not need these two helper function if you use something like Moment.js, but I won't impose an extra dependency on your project.
function stringToDate(stringDate) {
const parts = stringDate.split('-').map((p) => parseInt(p));
parts[1] -= 1;
return new Date(...parts.reverse());
}
function dateToString(date) {
return `${('0' + date.getDate()).slice(-2)}-${('0' + (date.getMonth() + 1)).slice(-2)}-${date.getFullYear()}`;
}
I've also added a sorter function that makes sure the ranges are sorted in an ascending fashion (smaller ranges first).
function dateRangeSorter(a, b) {
if (a[0] < b[0]) return -1;
else if (a[0] > b[0]) return 1;
if (a[1] < b[1]) return -1;
else if (a[1] > b[1]) return 1;
return 0;
}
Now we're good to go on the calculation, here is a code snippet that will log the output at the end.
// data
const output = [];
const oneDayInMs = 24 * 60 * 60 * 1000;
const mainDateRange = ['01-01-2020', '14-06-2020'];
const selectedRanges = [
['03-01-2020','04-01-2020'],
['03-01-2020','05-01-2020'],
['11-01-2020','13-01-2020'],
['01-02-2020','20-02-2020'],
['15-03-2020','18-03-2020'],
['06-01-2020','06-01-2020'],
['03-01-2020','04-01-2020']
];
// helpers
function stringToDate(stringDate) {
const parts = stringDate.split('-').map((p) => parseInt(p));
parts[1] -= 1;
return new Date(...parts.reverse());
}
function dateToString(date) {
return `${('0' + date.getDate()).slice(-2)}-${('0' + (date.getMonth() + 1)).slice(-2)}-${date.getFullYear()}`;
}
function dateRangeSorter(a, b) {
if (a[0] < b[0]) return -1;
else if (a[0] > b[0]) return 1;
if (a[1] < b[1]) return -1;
else if (a[1] > b[1]) return 1;
return 0;
}
// transform into date and sort
const mainDateRangeAsDates = mainDateRange.map(stringToDate);
const selectedRangesAsDates = selectedRanges.map((range) => (range.map(stringToDate)))
.sort(dateRangeSorter);
// start at the beginning of the main date range
let movingDate = mainDateRangeAsDates[0];
// loop through the selected ranges
selectedRangesAsDates.forEach(([startDate, endDate]) => {
// if there's a gap, add it to the output
if (movingDate < startDate) {
output.push([
dateToString(movingDate),
dateToString(new Date(startDate.getTime() - oneDayInMs))
]);
}
// move the cursor date to one day after the end of current rage
movingDate = new Date(endDate.getTime() + oneDayInMs);
});
// if there is a gap at the end, add it as well
if (movingDate < mainDateRangeAsDates[1]) {
output.push([
dateToString(movingDate),
dateToString(mainDateRangeAsDates[1])
]);
}
console.log(output);
Used a similar approach to this: How to make sure every number of a bigger range is within some smaller ranges?
Convert all strings to Dates. Sorts by minimum of range.
Moves minimum position forward, until it finds a gap, and pushes to res array.
Pushes range from last minimum to maximum if it exists
mainDateRange = ['01-01-2020', '14-06-2020'];
selectedRanges = [
['03-01-2020', '04-01-2020'],
['03-01-2020', '05-01-2020'], //overlapping dates
['11-01-2020', '13-01-2020'],
['01-02-2020', '20-02-2020'],
['15-03-2020', '18-03-2020'],
['06-01-2020', '06-01-2020'], //date ranges will not be ordered
['03-01-2020', '04-01-2020']
]; //dates that the member has work assigned
function gapFinder(mainDateRange, selectedRanges) {
const dateToInt = a => new Date(a.split('-').reverse().join('-'))
const intToDate = a => new Date(a).toISOString().slice(0, 10).split('-').reverse().join('-')
// convert to numbers
selectedRanges = selectedRanges.map(r => r.map(dateToInt))
// presort ranges
selectedRanges.sort(([a, ], [b, ]) => a - b)
let [min, max] = mainDateRange.map(dateToInt)
const res = []
for (const [x, y] of selectedRanges) {
if (min > max) break
if (min < x)
res.push([min, x.setDate(x.getDate() - 1)])
min = Math.max(min, y.setDate(y.getDate() + 1))
}
if (min <= max) res.push([min, max])
return res.map(r => r.map(intToDate))
}
console.log(JSON.stringify(gapFinder(mainDateRange,selectedRanges)))
selectedRanges.push(['11-06-2020', '13-06-2020'])
console.log(JSON.stringify(gapFinder(mainDateRange,selectedRanges)))
As the topic states what is the best way to make it so that when you pass an array of emotions/values, to show the closest value based on a numeric mapping in javascript?.
Assume that 'Glad' is the same thing as 'Happy', and 'Down' is the same thing as 'Sad'. Ithe code I've tried seems incredibly lengthy and gets bloated if I add more emotions/states (i.e. Angry). Aside from the emotions array, any new functions and data structures and variables can be changed/introduced.
for example, I can get a list of emotions:
let emotions = ['Happy','Happy','Sad','Glad','Angry'];
Now I want to return a string that reflects what the 'closest' emotion based on these 5 emotions.
For a better example, let's assume the values correspondent to each emotion is:
Angry = 1, Happy = 2, Sad = 3
I was trying something like:
var numb = 0;
for (var i = 0; i < emotions.length; i++) {
if (numb == 'Angry')
numb += 1;
if (numb == 'Happy' || numb == 'Glad')
numb += 2;
if (numb == 'Sad' || numb == 'Down')
numb += 3;
}
var average = numb / emotions.length;
// check which number is closer to
if (average < 1.5)
return 'Angry';
if (average >= 1.5 && < 2.5)
return 'Happy';
if (average > 2.5)
return 'Sad';
if (average == 1.5)
return 'Angry or Happy';
if (average == 2.5)
return 'Happy or Sad';
My expected result based on this list of emotions is:
2(*Happy*) + 2(*Happy*) + 3(*Sad*) + 2(*Happy|Glad*) + 1(*Angry*) = 10
Then divide by 5 (the emotions array length), resulting in 2.
So the result that should be returned, as string, is "Happy".
Let's say I added a fourth type of emotion/feeling... I would be adding more and more of these conditions, and it gets more complicated in the logic checking for the ranges of the numbers.
I am looking at the list of emotions as a whole, and trying to come up with an overall emotion that represents the whole list.
What is the best way to do this so that the code looks clean and I can support more states without having the lines of code become too long?
What about something like this:
Having two object constants:
emotionsValues: Here you assing a value to each emotion you want, like a score to each.
emotionsRank: Here is the final result of each value, based on average you'll get the result from here.
Now:
Receive the emotions array by parameter.
reduce it based on the value of each mapped emotion (using emotionsValues).
Get the average
See if the floor value + ceil value divided by 2 is equal to the number itself (it means its exactly the half), so use the "emotion or emotion".
OR, if not the half, then round to the nearest and get the correct emotion. Don't forget to check if average is below 1 or bigger the the last rank (3 in this case)
const emotionsValues = {
"Angry": 1,
"Happy": 2,
"Glad": 2,
"Sad": 3,
"Down": 3,
}
const emotionsRank = {
1: "Angry",
2: "Happy",
3: "Sad",
}
function getEmotion(arrayEmot) {
let numb = arrayEmot.reduce((acc, v) => Number(emotionsValues[v]) + acc, 0);
let avg = numb / arrayEmot.length;
let min = Math.floor(avg)
let max = Math.ceil(avg)
if ((min + max) / 2 == avg && min != max) {
return emotionsRank[min] + " or " + emotionsRank[max]
} else {
let rounded = avg < 1 ? 1 : avg > 3 ? 3 : Math.round(avg);
return emotionsRank[rounded];
}
}
let emotionsTest = ['Happy', 'Happy', 'Sad', 'Glad', 'Angry'];
console.log(getEmotion(emotionsTest))
let emotionsTest2 = ['Happy', 'Happy', 'Sad', 'Sad'];
console.log(getEmotion(emotionsTest2))
You may create the function emo to value and its reciprocal one: value to emotionS:
Then you map every emotions found in array to its value
do your standard mathematical stuff
and get back to emotions via the reciprocal function
const emoToValue = {
Glad: 1,
Happy: 1,
Sad: 2
}
const valueToEmos = Object.entries(emoToValue).reduce((acc, [emo, val]) => {
acc[val] = acc[val] || []
acc[val].push(emo)
return acc
}, {})
//compute the average:
function avgEmotion (emotions) {
if (emotions.length == 0) return ''
const avg = emotions.reduce((s, em) => s + emoToValue[em], 0) / emotions.length
return valueToEmos[Math.round(avg)].join(' or ')
}
console.log('str', avgEmotion(['Happy', 'Happy', 'Sad', 'Happy'])) //Glad or Happy
console.log('str', avgEmotion(['Happy', 'Happy', 'Sad', 'Sad'])) //Sad
This function explicitly checks for the "mid" case and also for out of range values (since it's based on indices):
function getEmotion(emotions, value) {
// Out of range
if ( value > emotions.length ) return emotions[emotions.length - 1];
if ( value < 1 ) return emotions[0];
// Determine if decimal is .5
let mid = value % 1 === .5;
// Round the value to the nearest integer
let rounded = Math.round(value);
return mid ? `${emotions[rounded - 2]} or ${emotions[rounded - 1]}` : emotions[rounded - 1];
}
Output:
let emotions = ['Happy', 'Happy', 'Sad', 'Glad', 'Angry'];
console.log(getEmotion(emotions, -23)); // Happy
console.log(getEmotion(emotions, 0)); // Happy
console.log(getEmotion(emotions, 1)); // Happy
console.log(getEmotion(emotions, 2.43)); // Happy
console.log(getEmotion(emotions, 2.5)); // Happy or Sad
console.log(getEmotion(emotions, 3.1)); // Sad
console.log(getEmotion(emotions, 155.65)); // Angry
You could create a set of indices and get the values by filtering with the index.
function getEmotion(emotions, value) {
var values = new Set([value + 0.5, value - 0.5, Math.round(value)]);
return emotions.filter((e, i) => values.has(i + 1)).join(' and ');
}
console.log(getEmotion(['Happy', 'Sad', 'Glad', "Angry"], 1));
console.log(getEmotion(['Happy', 'Sad', 'Glad', "Angry"], 1.5));
console.log(getEmotion(['Happy', 'Sad', 'Glad', "Angry"], 1.7));
I'm using LocalStorage to save an array of Dates and Costs.
When I'm writing localStorage.getItem("todos"); into the console, the format will be like this:
"[{"due":"28/10/2017","task":"80"},{"due":"06/10/2017","task":"15"}]"
Where due is the Date, and TASK is the AMOUNT.
I managed to get the TOTAL of AMOUNTS by:
total: {
type: String,
value: () => {
var values = localStorage.getItem("todos");
if (values === undefined || values === null) {
return "0";
}
var data = JSON.parse(values);
var sum = 0;
data.forEach(function(ele){ sum+=Number(ele.task)}); return sum;
}
}
Now I'm trying to get the TOTAL of last 6 MONTHS.
I have no idea on how to approach this.
How should I be able to do this?
During your iteration you need to add a check to make sure the sum is only including values where the due date is within your range. If you can use a library like moment, this would greatly simplify your logic.
const data = [
{ due: '28/10/2017', task: 80 },
{ due: '06/10/2017', task: 15 },
{ due: '10/05/2000', task: 3000 }
];
const sixMonthsAgo = moment().subtract(6, 'months');
const total = data.reduce((acc, item) => {
const dueDate = moment(item.due, 'DD/MM/YYYY');
return acc + (dueDate.isAfter(sixMonthsAgo) ? item.task : 0);
}, 0);
console.log('total should equal 95: ', total);
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.19.1/moment.min.js"></script>
Here is a solution for your issue :
make a test in the forEach loop :
I've put 4 dates : 2 under 6 months and 2 older
The result is 80+15 = 95
// After JSON.parse
var todos=[{"due":"28/10/2017","task":"80"},{"due":"06/10/2017","task":"15"},{"due":"06/04/2017","task":"15"},{"due":"06/02/2017","task":"15"}];
var sum = 0;
var minDate = new Date();
var month = minDate.getMonth()+1-6; // get month minus 6 months
var year = minDate.getFullYear(); // get year
if(month < 1){ // if month is under January then change year
month+=6;
year-= 1;
}
minDate.setMonth(month); // Replace our min date with our - 6 m
minDate.setYear(year); // set year in case we have changed
todos.forEach(function(ele){
var arr = ele.due.split("/"); // split french string date into d,m,y
if(arr.length==3){
var dueDate = new Date(arr[2],arr[1],arr[0]); // get the task date
if(dueDate>minDate){ // if task is not to old then
sum+=parseInt(ele.task); // sum it
}
}
});
console.log(sum);