I have this code:
var dates1 = ['1/2021', '1/2021', '12/2020'];
var values1 = ['-2500', '-150', '-10000'];
var dates2 = ['2/2021', '3/2021', '1/2021'];
var values2 = ['3000', '1000', '3000'];
What I need is to merge the dates1 with dates2, and keep the same order for values1 and values2 adding a 0 for the dates that has none values, so the result would be this:
var dates = ['12/2020', '1/2021', '2/2021', '3/2021'];
var values1 = ['-10000', '-2650', '0', '0'];
var values2 = ['0', '3000', '3000', '1000'];
To merge the arrays of dates I'm using this code:
var dates = dates1.concat(dates2);
I just don't know how can I keep the same order for values1 and values2 adding a 0 for none values.
Any suggestion ? Thank you !
Break down the algorithm into the smallest steps, then order the steps after each other:
const dates1 = ['1/2021', '1/2021', '12/2020'];
const values1 = ['-2500', '-150', '-10000'];
const dates2 = ['2/2021', '3/2021', '1/2021'];
const values2 = ['3000', '1000', '3000'];
// summing the arrays & keeping track of how
// the values should be ordered
const reduceArr = ({ dates, values }) => {
const reduced = dates.reduce((a, c, i) => {
if (typeof a[c] === 'undefined') a[c] = 0
a[c] += Number(values[i])
return a
}, {})
const filteredDates = [...new Set([...dates])]
const filteredValues = filteredDates.map(date => reduced[date])
return {
filteredDates,
filteredValues,
}
}
// merging the different dates arrays
const mergeDates = ({ dates1, dates2 }) => {
return [...new Set([...dates1, ...dates2])]
}
// time-sorting the merged arrays
const sortDates = ({ dates }) => {
return [...dates].sort((a, b) => {
const [m1, y1] = a.split('/')
const [m2, y2] = b.split('/')
return new Date(y1, m1, 1) - new Date(y2, m2, 1)
})
}
// mapping values based on the orders &
// adding 0 if no value is found
const mapToDates = ({ sortedDates, reducedArr }) => {
return sortedDates.map(date => {
const idx = reducedArr.filteredDates.indexOf(date)
return idx === -1 ? 0 : reducedArr.filteredValues[idx]
})
}
// actually executing the steps:
const mergedDates = mergeDates({ dates1, dates2 })
const sortedDates = sortDates({ dates: mergedDates })
const reducedArr1 = reduceArr({ dates: dates1, values: values1 })
const mapValues1 = mapToDates({ sortedDates, reducedArr: reducedArr1 })
const reducedArr2 = reduceArr({ dates: dates2, values: values2 })
const mapValues2 = mapToDates({ sortedDates, reducedArr: reducedArr2 })
console.log('mapValues1', mapValues1)
console.log('mapValues2', mapValues2)
I think that what you need is that:
Array.prototype.unique = function() {
var a = this.concat();
for(var i=0; i<a.length; ++i) {
for(var j=i+1; j<a.length; ++j) {
if(a[i] === a[j])
a.splice(j--, 1);
}
}
return a;
};
stringToDate = function(str) {
return str.substring(str.search("/")+1, str.search("/")+5) + "-" + (Number(str.substring(0, str.search("/"))) < 10 ? '0' : '') + str.substring(0, str.search("/") ) + "-15T00:00:00Z";
}
dateToString = function(dt) {
dt = new Date(dt);
return (1 + dt.getMonth()) + "/" + dt.getFullYear() ;
}
var dates1 = [stringToDate('1/2021'), stringToDate('1/2021'), stringToDate('12/2020')];
var values1 = ['-2500', '-150', '-10000'];
var dates2 = [stringToDate('2/2021'), stringToDate('3/2021'), stringToDate('1/2021')];
var values2 = ['3000', '1000', '3000'];
var dates_out = dates1.concat(dates2).unique().sort();
var values1_out = new Array(dates_out.length);
var values2_out = new Array(dates_out.length);
dates_out.forEach((dt, i) => {
dates_out[i] = dateToString(dates_out[i]);
values1_out[i] = 0;
values2_out[i] = 0;
dates1.forEach((dt1, i1) => {
if (dt1 === dt) {
if (values1_out[i] != undefined)
values1_out[i] = values1_out[i] + Number(values1[i1]);
else
values1_out[i] = Number(values1[i1]);
}
});
dates2.forEach((dt2, i2) => {
if (dt2 === dt) {
if (values2_out[i] != undefined)
values2_out[i] = values2_out[i] + Number(values2[i2]);
else
values2_out[i] = Number(values2[i2]);
}
});
});
console.log(dates_out);
console.log(values1_out);
console.log(values2_out);
I don't know if this is the best solution. I would create dictionaries to work with the data. I understood that you need to order the dates (the first result being 12/2020 instead of 1/2021). I also understood that you need the dates as a string, but if you need the date as a datatype, you can remove the part where I convert it back to a string.
here is the solution in python, conversion to javascript should be straight forward. 1. build a list of tuples for dates1/values1 and dates2/values2. 2. Get a list of unique dates. 3. Reduce the tuple lists to a dictionary accumulating on the key which is the date. 4. Using the dates list and the dictionaries create the result value1 and 2 list.
def ReduceToDictionary(tuples):
dict={}
for item in tuples:
key = item[0]
value = item[1]
if key in dict:
dict[key] += float(value)
else:
dict[key] = float(value)
return dict
def BuildList(dates,dict):
result_values=[]
for date in dates:
if date in dict:
val=dict[date]
result_values.append(val)
else:
val=0
result_values.append(val)
return result_values
def StrToDate(string):
groups=re.match('(\d{1,2})[/](\d{4})',string)
year=int(groups[2])
month=int(groups[1])
return(datetime.datetime(year,month,1))
dates1 = ['1/2021', '1/2021', '12/2020']
values1 = ['-2500', '-150', '-10000']
dates2 = ['2/2021', '3/2021', '1/2021']
values2 = ['3000', '1000', '3000']
tuple1=[(StrToDate(dates1[i]),values1[i]) for i in range(len(dates1))]
tuple2=[(StrToDate(dates2[i]),values2[i]) for i in range(len(dates2))]
dates=sorted(set(list(map(StrToDate,dates1))+list(map(StrToDate,dates2))))
dict1=ReduceToDictionary(tuple1)
dict2=ReduceToDictionary(tuple2)
result_values1=BuildList(dates,dict1)
result_values2=BuildList(dates,dict2)
date_string=[date.strftime("%Y/%m") for date in dates]
print(date_string)
print(result_values1)
print(result_values2)
output:
['2020/12', '2021/01', '2021/02', '2021/03']
[-10000.0, -2650.0, 0, 0]
[0, 3000.0, 3000.0, 1000.0]