英文:
What would be an efficient algo in JavaScript to merge date time series?
问题
我有以下时间序列列表作为输入:
const ts1 = [
['2023-01-20', 1],
['2023-01-21', 2],
['2023-01-22', 3],
['2023-01-23', 4],
];
const ts2 = [
['2023-01-18', 5],
['2023-01-19', 6],
['2023-01-20', 7],
['2023-01-21', 8]
];
const ts3 = [
['2023-01-21', 9],
['2023-01-22', 10],
['2023-01-23', 11],
['2023-01-24', 12]
];
我希望输出是合并(列堆叠)的版本,如下所示:
const output = [
['2023-01-18', null, 5, null],
['2023-01-19', null, 6, null],
['2023-01-20', 1, 7, null],
['2023-01-21', 2, 8, 9],
['2023-01-22', 3, null, 10],
['2023-01-23', 4, null, 11],
['2023-01-24', null, null, 12]
];
为了提供更多上下文,我正在重用一个提供单个时间序列的REST API,我需要编译成AnyChart所需的tableData格式。
英文:
I have the following time series list as input:
const ts1 = [
['2023-01-20', 1],
['2023-01-21', 2],
['2023-01-22', 3],
['2023-01-23', 4],
];
const ts2 = [
['2023-01-18', 5],
['2023-01-19', 6],
['2023-01-20', 7],
['2023-01-21', 8]
];
const ts3 = [
['2023-01-21', 9],
['2023-01-22', 10],
['2023-01-23', 11],
['2023-01-24', 12]
];
I'd like the output to be the merged (column stacked) version like so:
const output = [
['2023-01-18', null, 5, null],
['2023-01-19', null, 6, null],
['2023-01-20', 1, 7, null],
['2023-01-21', 2, 8, 9],
['2023-01-22', 3, null, 10],
['2023-01-23', 4, null, 11],
['2023-01-24', null, null, 12]
];
To give a bit more context, I'm reusing a REST API which provides the individual time-series and I need to compile the AnyChart required tableData format.
答案1
得分: 1
以下是翻译好的部分:
// 创建一个使用`Array#reduce`和nullish coalescing assignment的替代方法:
const ts1 = [["2023-01-20", 1], ["2023-01-21", 2], ["2023-01-22", 3], ["2023-01-23", 4]];
const ts2 = [["2023-01-18", 5], ["2023-01-19", 6], ["2023-01-20", 7], ["2023-01-21", 8]];
const ts3 = [["2023-01-21", 9], ["2023-01-22", 10], ["2023-01-23", 11], ["2023-01-24", 12]];
const res = Object.entries([ts1, ts2, ts3].reduce((acc, curr, i, t) => {
curr.forEach(([k, v]) => (acc[k] ??= Array(t.length).fill(null))[i] = v);
return acc;
}, {})).sort().map(([k, v]) => [k, ...v]);
console.log(res);
注意:我已经移除了HTML标记并进行了格式化以提供更清晰的代码。
英文:
An alternative method with Array#reduce
and nullish coalescing assignment:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
const ts1=[["2023-01-20",1],["2023-01-21",2],["2023-01-22",3],["2023-01-23",4],],ts2=[["2023-01-18",5],["2023-01-19",6],["2023-01-20",7],["2023-01-21",8]],ts3=[["2023-01-21",9],["2023-01-22",10],["2023-01-23",11],["2023-01-24",12]];
const res = Object.entries([ts1, ts2, ts3].reduce((acc, curr, i, t) => {
curr.forEach(([k, v]) => (acc[k] ??= Array(t.length).fill(null))[i] = v);
return acc;
}, {})).sort().map(([k, v]) => [k, ...v]);
console.log(res);
<!-- end snippet -->
答案2
得分: 1
你可以创建一个高效的算法来合并时间序列,首先将所有的日期和数值放入一个哈希映射中,然后将其转换为一个数组。
这是一个高效的方法,因为哈希映射操作(插入和查找)通常是 O(1) 的,这使得该算法比直接比较和插入数组更快。
这里是一个例子:
const ts1 = [
['2023-01-20', 1],
['2023-01-21', 2],
['2023-01-22', 3],
['2023-01-23', 4],
];
const ts2 = [
['2023-01-18', 5],
['2023-01-19', 6],
['2023-01-20', 7],
['2023-01-21', 8]
];
const ts3 = [
['2023-01-21', 9],
['2023-01-22', 10],
['2023-01-23', 11],
['2023-01-24', 12]
];
function mergeTimeSeries(...timeSeries) {
const hashMap = {};
let seriesIndex = 1;
for (const ts of timeSeries) {
for (const [date, value] of ts) {
if (!hashMap[date]) {
hashMap[date] = Array(timeSeries.length + 1).fill(null);
hashMap[date][0] = date;
}
hashMap[date][seriesIndex] = value;
}
seriesIndex++;
}
// 将哈希映射转换为数组并按日期排序
const result = Object.values(hashMap).sort((a, b) => new Date(a[0]) - new Date(b[0]));
return result;
}
const output = mergeTimeSeries(ts1, ts2, ts3);
console.log(output);
英文:
You can create an efficient algorithm to merge the time series by first putting all the dates and values in a hash map and then converting it into an array.
This is an efficient approach because hash map operations (insertion and lookup) are generally O(1), which makes the algorithm faster than directly comparing and inserting into arrays.
Here is an example:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
const ts1 = [
['2023-01-20', 1],
['2023-01-21', 2],
['2023-01-22', 3],
['2023-01-23', 4],
];
const ts2 = [
['2023-01-18', 5],
['2023-01-19', 6],
['2023-01-20', 7],
['2023-01-21', 8]
];
const ts3 = [
['2023-01-21', 9],
['2023-01-22', 10],
['2023-01-23', 11],
['2023-01-24', 12]
];
function mergeTimeSeries(...timeSeries) {
const hashMap = {};
let seriesIndex = 1;
for (const ts of timeSeries) {
for (const [date, value] of ts) {
if (!hashMap[date]) {
hashMap[date] = Array(timeSeries.length + 1).fill(null);
hashMap[date][0] = date;
}
hashMap[date][seriesIndex] = value;
}
seriesIndex++;
}
// Convert hashMap into array and sort by date
const result = Object.values(hashMap).sort((a, b) => new Date(a[0]) - new Date(b[0]));
return result;
}
const output = mergeTimeSeries(ts1, ts2, ts3);
console.log(output);
<!-- end snippet -->
答案3
得分: 1
这也可以在没有Object.entries和keys等的情况下完成。
例如...
let res = [], pos = {};
[ts1, ts2, ts3].forEach((a, i, arr) =>
a.forEach(([k, v]) => {
pos[k] = pos[k] || (res[res.length] = [k, ...arr.map(_ => null)]);
pos[k][i + 1] = v;
}));
console.log(res.sort());
英文:
This could also be done without the Object.entries and keys etc.
For example...
let res = [], pos = {};
[ts1, ts2, ts3].forEach((a, i, arr) =>
a.forEach(([k, v]) => {
pos[k] = pos[k] || (res[res.length] = [k, ...arr.map(_ => null)]);
pos[k][i + 1] = v;
}));
console.log(res.sort());
答案4
得分: 1
我们可以使用双重归并将其转换为如下形式:
{
"2023-01-20": [1, 7, null],
"2023-01-21": [2, 8, 9],
"2023-01-22": [3, null, 10],
/* ... */
}
然后将其传递给 Object.entries()
,对结果进行排序,并将每个条目组合成单个数组。以下是这个版本:
const mergeTimeSeries = (tss) => Object.entries(tss.reduce((r, ts, i) => ts.reduce(
(r, [d, v]) => ({
...r, [d]: Object.assign((r[d] ?? Array(tss.length).fill(null)), {[i]: v})
}), r), {}
)).sort().map(([k, vs]) => [k, ...vs])
const ts1 = [['2023-01-20', 1], ['2023-01-21', 2], ['2023-01-22', 3], ['2023-01-23', 4]], ts2 = [['2023-01-18', 5], ['2023-01-19', 6], ['2023-01-20', 7], ['2023-01-21', 8]], ts3 = [['2023-01-21', 9], ['2023-01-22', 10], ['2023-01-23', 11], ['2023-01-24', 12]]
console.log(mergeTimeSeries([ts1, ts2, ts3]))
我们的 sort
调用利用了数组被转换为字符串的方式。它可以工作,但感觉有点奇怪。但如果你想的话,你可以始终传递一个更明确的排序函数:([a], [b]) => a < b ? -1 : a > b ? 1 : 0
。
英文:
We can use a double reduction to turn this into something like:
{
"2023-01-20": [1, 7, null],
"2023-01-21": [2, 8, 9],
"2023-01-22": [3, null, 10],
/* ... */
}
then pass this to Object.entries()
, sort the result, and combine each entry into a single array. Here is a version of this:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
const mergeTimeSeries = (tss) => Object.entries(tss.reduce((r, ts, i) => ts.reduce(
(r, [d, v]) => ({
...r, [d]: Object.assign((r[d] ?? Array(tss.length).fill(null)), {[i]: v})
}), r), {}
)).sort().map(([k, vs]) => [k, ...vs])
const ts1 = [['2023-01-20', 1], ['2023-01-21', 2], ['2023-01-22', 3], ['2023-01-23', 4]], ts2 = [['2023-01-18', 5], ['2023-01-19', 6], ['2023-01-20', 7], ['2023-01-21', 8]], ts3 = [['2023-01-21', 9], ['2023-01-22', 10], ['2023-01-23', 11], ['2023-01-24', 12]]
console.log(mergeTimeSeries([ts1, ts2, ts3]))
<!-- language: lang-css -->
.as-console-wrapper {max-height: 100% !important; top: 0}
<!-- end snippet -->
Our sort
calls takes advantage of how the arrays are turned into strings. It works, but feels odd. But if you want, you can always pass a more explicit function to sort: ([a], [b]) => a < b ? -1 : a > b ? 1 : 0
.
答案5
得分: 0
我会选择类似以下的代码:
const merged = {};
let tses = [ts1, ts2, ts3];
for (let i = 0; i < tses.length; i++) {
for (const [t, v] of tses[i]) {
if (!merged[t]) merged[t] = new Array(tses.length).fill(null);
merged[t][i] = v;
}
}
console.log(Object.entries(merged).map(([t, vs]) => [t, ...vs]));
这会输出:
[
[ '2023-01-20', 1, 7, null ],
[ '2023-01-21', 2, 8, 9 ],
[ '2023-01-22', 3, null, 10 ],
[ '2023-01-23', 4, null, 11 ],
[ '2023-01-18', null, 5, null ],
[ '2023-01-19', null, 6, null ],
[ '2023-01-24', null, null, 12 ]
]
按照排序顺序(因为键是按字母顺序排序的),可以使用以下方式:
console.log(Object.keys(merged).sort().map(key => [key, ...merged[key]]));
来得到以下结果:
[
[ '2023-01-18', null, 5, null ],
[ '2023-01-19', null, 6, null ],
[ '2023-01-20', 1, 7, null ],
[ '2023-01-21', 2, 8, 9 ],
[ '2023-01-22', 3, null, 10 ],
[ '2023-01-23', 4, null, 11 ],
[ '2023-01-24', null, null, 12 ]
]
英文:
I'd go with something like
const merged = {};
let tses = [ts1, ts2, ts3];
for (let i = 0; i < tses.length; i++) {
for (const [t, v] of tses[i]) {
if (!merged[t]) merged[t] = new Array(tses.length).fill(null);
merged[t][i] = v;
}
}
console.log(Object.entries(merged).map(([t, vs]) => [t, ...vs]));
This outputs
[
[ '2023-01-20', 1, 7, null ],
[ '2023-01-21', 2, 8, 9 ],
[ '2023-01-22', 3, null, 10 ],
[ '2023-01-23', 4, null, 11 ],
[ '2023-01-18', null, 5, null ],
[ '2023-01-19', null, 6, null ],
[ '2023-01-24', null, null, 12 ]
]
as expected.
For sorted order (since the keys are lexicographically sortable), do
console.log(Object.keys(merged).sort().map(key => [key, ...merged[key]]));
instead:
[
[ '2023-01-18', null, 5, null ],
[ '2023-01-19', null, 6, null ],
[ '2023-01-20', 1, 7, null ],
[ '2023-01-21', 2, 8, 9 ],
[ '2023-01-22', 3, null, 10 ],
[ '2023-01-23', 4, null, 11 ],
[ '2023-01-24', null, null, 12 ]
]
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论