我需要一个在Java数组上滑动的窗口。
以下是我的尝试,因为我找不到现成的解决方案:
function window(a,sz) { return a.map((_,i,ary)=> ary.slice(i,i+sz)).slice(0,-sz+1); }
它返回一个窗口数组,可以将其映射到各个窗口上。
什么是更好的解决方案?
答案 0 :(得分:2)
通过它们的原型添加到本机JavaScript对象不是一个好主意。这可能会以意想不到的方式破坏事物,并给您和其他使用您的代码的人带来很多挫败感。在这种情况下,最好创建自己的函数。
要获得所需的功能,只需将数组传递给函数,然后从那里访问它。通过函数在数组上进行所需的方法调用。遵循KISS的原则,这里不再需要花哨的东西。
此外,请记住,将为数组的每个元素调用Array.map。这不是您真正需要的。如果目标是获得大小为n的滑动窗口,并且希望将每个窗口添加到新数组中,则可以使用如下函数:
var myArray = [1, 2, 3, 4, 5, 6, 7, 8];
function getWindows(arr, n){
if (n > arr.length)
{
return arr;
}
var result = [];
let lastWindow = i < arr.length/n - n;
for (let i = 0; i < lastWindow; i += n)
{
result.push(arr.slice(i, i + n));
}
return result;
}
因此,在这里,我们将获得一个窗口数组,这些窗口也是数组。调用console.log(getWindows(myArray, 3))
,将得到以下输出:
0:Array(3)[1,2,3]
1:Array(3)[4,5,6]
2:Array(3)[7,8,9]
答案 1 :(得分:1)
使用JS ES6,您可以执行以下操作:
class SlidingWindow{
constructor(windowSize) {
this.deque = []; // for storing the indexex of the 'k' elements in the input
this.windowSize = windowSize;
}
compute(input){
let output = [];
if(!input || input.length === 0) {
return [];
}
if(input.length < this.windowSize) {
return input;
}
for(let i=0; i < input.length; i++) {
//if the index in the first element of the this.deque is out of bound (i.e. idx <= i-this.windowSize) then remove it
if(this.deque.length > 0 && this.deque[0] === i-this.windowSize) {
this.deque.shift(); //remove the first element
}
this.deque.push(i)
if(i+1 >= this.windowSize) {
output.push(this.deque.map(idx => input[idx]));
}
}
return output;
}
}
//Here is how to use it:
let slidingWindow = new SlidingWindow(3);
console.log('computed sliding windows: '+JSON.stringify(slidingWindow.compute([1,2,3,4,5,6,7,8,9])));
要计算每个滑动窗口的最大值,可以按以下方式自定义上面的代码:
class SlidingWindow{
constructor(windowSize) {
this.deque = []; // for storing the indexex of the 'k' elements in the input
this.windowSize = windowSize;
}
customCompute(input, processWindow, addOutput) {
let output = [];
if(!input || input.length === 0) {
return [];
}
if(input.length < this.windowSize) {
return input;
}
for(let i=0; i < input.length; i++) {
//if the index in the first element of the this.deque is out of bound (i.e. idx <= i-this.windowSize) then remove it
if(this.deque.length > 0 && this.deque[0] === i-this.windowSize) {
this.deque.shift(); //remove the first element
}
processWindow(this.deque, input[i], input)
this.deque.push(i)
if(i+1 >= this.windowSize) {
output.push(addOutput(this.deque, input));
}
}
this.deque = [];
return output;
}
}
let slidingWindow = new SlidingWindow(3);
console.log('computed sliding windows: '+JSON.stringify(slidingWindow.compute([1,2,3,4,5,6,7,8,9])));
function processWindow(deque, currentElement, input){
while(deque.length > 0 && currentElement > input[deque[deque.length -1]]) {
deque.pop(); //remove the last element
}
};
console.log('computed sliding windows maximum: '+JSON.stringify(slidingWindow.customCompute([1,3,-1,-3,5,3,6,7], processWindow, (deque, input) => input[deque[0]])));
答案 2 :(得分:0)
Array#reduce
避免.map
后跟.slice()
的合理选择是使用.reduce()
生成窗口:
function toWindows(inputArray, size) {
return inputArray
.reduce((acc, _, index, arr) => {
if (index+size > arr.length) {
//we've reached the maximum number of windows, so don't add any more
return acc;
}
//add a new window of [currentItem, maxWindowSizeItem)
return acc.concat(
//wrap in extra array, otherwise .concat flattens it
[arr.slice(index, index+size)]
);
}, [])
}
const input = [1, 2, 3, 4, 5, 6, 7, 8, 9];
//JSON.stringify to produce more compact result in the console
console.log(JSON.stringify(toWindows(input, 2)));
console.log(JSON.stringify(toWindows(input, 3)));
console.log(JSON.stringify(toWindows(input, 4)));
console.log(JSON.stringify(toWindows(input, 9)));
console.log(JSON.stringify(toWindows(input, 10)));
//somewhat more realistic usage:
//find the maximimum odd sum when adding together three numbers at a time
const output = toWindows([ 3, 9, 1, 2, 5, 4, 7, 6, 8 ], 3)
.map(window => window.reduce((a,b) => a+b)) //sum
.filter(x => x%2 === 1) //get odd
.reduce((highest, current) => Math.max(highest, current), -Infinity) //find highest
console.log(output)
然后可以根据需要将其缩短:
function toWindows(inputArray, size) {
return inputArray
.reduce(
(acc, _, index, arr) => (index+size > arr.length) ? acc : acc.concat([arr.slice(index, index+size)]),
[]
)
}
const input = [1, 2, 3, 4, 5, 6, 7, 8, 9];
//JSON.stringify to produce more compact result in the console
console.log(JSON.stringify(toWindows(input, 2)));
console.log(JSON.stringify(toWindows(input, 3)));
console.log(JSON.stringify(toWindows(input, 4)));
console.log(JSON.stringify(toWindows(input, 9)));
console.log(JSON.stringify(toWindows(input, 10)));
//somewhat more realistic usage:
//find the maximimum odd sum when adding together three numbers at a time
const output = toWindows([ 3, 9, 1, 2, 5, 4, 7, 6, 8 ], 3)
.map(window => window.reduce((a,b) => a+b)) //sum
.filter(x => x%2 === 1) //get odd
.reduce((highest, current) => Math.max(highest, current), -Infinity) //find highest
console.log(output);
Array.from
可以使用Array.from
来简化方法,首先生成具有适当长度的数组,然后使用生成的窗口填充该数组:
function toWindows(inputArray, size) {
return Array.from(
{length: inputArray.length - (size - 1)}, //get the appropriate length
(_, index) => inputArray.slice(index, index+size) //create the windows
)
}
const input = [1, 2, 3, 4, 5, 6, 7, 8, 9];
//JSON.stringify to produce more compact result in the console
console.log(JSON.stringify(toWindows(input, 2)));
console.log(JSON.stringify(toWindows(input, 3)));
console.log(JSON.stringify(toWindows(input, 4)));
console.log(JSON.stringify(toWindows(input, 9)));
console.log(JSON.stringify(toWindows(input, 10)));
//somewhat more realistic usage:
//find the maximimum odd sum when adding together three numbers at a time
const output = toWindows([ 3, 9, 1, 2, 5, 4, 7, 6, 8 ], 3)
.map(window => window.reduce((a,b) => a+b)) //sum
.filter(x => x%2 === 1) //get odd
.reduce((highest, current) => Math.max(highest, current), -Infinity) //find highest
console.log(output)
另一种替代方法是使用生成器函数,而不是预先计算所有窗口。这对于使用滑动窗口方法进行更懒惰的评估可能很有用。如果需要,您仍然可以使用Array.from
计算所有窗口:
function* windowGenerator(inputArray, size) {
let index = 0;
while(index+size <= inputArray.length) {
yield inputArray.slice(index, index+size);
index++;
}
}
function toWindows(inputArray, size) {
//compute the entire sequence of windows into an array
return Array.from(windowGenerator(inputArray, size))
}
const input = [1, 2, 3, 4, 5, 6, 7, 8, 9];
//JSON.stringify to produce more compact result in the console
console.log(JSON.stringify(toWindows(input, 2)));
console.log(JSON.stringify(toWindows(input, 3)));
console.log(JSON.stringify(toWindows(input, 4)));
console.log(JSON.stringify(toWindows(input, 9)));
console.log(JSON.stringify(toWindows(input, 10)));
//somewhat more realistic usage:
//find the sum closest to a target number when adding three numbers at a time
const veryLargeInput = [17, 95, 27, 30, 32, 38, 37, 67, 53, 46, 33, 36, 79, 14, 19, 25, 3, 54, 98, 11, 68, 96, 89, 71, 34, 31, 28, 13, 99, 10, 15, 84, 48, 29, 74, 78, 8, 90, 50, 49, 59, 18, 12, 40, 22, 80, 42, 21, 73, 43, 70, 100, 1, 44, 56, 5, 6, 75, 51, 64, 58, 85, 91, 83, 24, 20, 72, 26, 88, 66, 77, 60, 81, 35, 69, 93, 86, 4, 92, 9, 39, 76, 41, 37, 63, 45, 61, 97, 2, 16, 57, 65, 87, 94, 52, 82, 62, 55, 7, 23];
const targetNumber = 100;
console.log(`-- finding the closest number to ${targetNumber}`)
const iterator = windowGenerator(veryLargeInput, 3);
let closest = -1;
for (const win of iterator) {
const sum = win.reduce((a, b) => a+b);
const difference = Math.abs(targetNumber - sum);
const oldDifference = Math.abs(targetNumber - closest);
console.log(
`--- evaluating: ${JSON.stringify(win)}
sum: ${sum},
difference with ${targetNumber}: ${difference}`
);
if (difference < oldDifference) {
console.log(`---- ${sum} is currently the closest`);
closest = sum;
if (difference === 0) {
console.log("----- prematurely stopping - we've found the closest number")
break;
}
}
}
console.log(`-- closest sum is: ${closest}`)
答案 3 :(得分:0)
您考虑过递归吗?
l
是每个窗口的大小xs
是您的列表i
是我们需要进行的迭代次数,xs.length - l
out
包含结果可以使用xs.slice(i, i + l)
获得切片。在每次递归时,i
都会递增,直到i
到达下一个切片将包含少于l
个元素的地步。
const windows = (l, xs, i = 0, out = []) =>
i > xs.length - l
? out
: windows(l, xs, i + 1, [...out, xs.slice(i, i + l)]);
console.log(windows(3, [1,2,3,4,5,6,7,8,9]));
还有一个flatMap
的非递归解决方案。
使用flatMap
,您可以在每次迭代时返回一个数组,它将在最终结果中展平:
const duplicate = xs => xs.flatMap(x => [x, x]);
duplicate([1, 2]);
//=> [1, 1, 2, 2]
因此,您可以返回切片(用[]
包装),直到i
超过限制xs.length - l
:
const windows = (l, xs) =>
xs.flatMap((_, i) =>
i <= xs.length - l
? [xs.slice(i, i + l)]
: []);
console.log(windows(3, [1,2,3,4,5,6,7,8,9]))
请注意,在诸如ramda.js之类的某些库中,这称为aperture
:
返回一个新列表,该列表由n个连续元素的元组组成。如果n大于列表的长度,则返回一个空列表。
aperture(3, [1,2,3,4,5,6,7,8,9]);
//=> [[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6], [5, 6, 7], [6, 7, 8], [7, 8, 9]]
您可以看到以前有几个人有相同的问题: