我想编写一个函数,从给定的起始索引中查找给定数组中的连续子数组,如果找到则返回数组中子数组的索引,如果找不到,则返回-1。这与String.indexOf
类似,但对于数组和子数组而不是字符串和子字符串。
这是我的工作代码:
var find_csa = function (arr, subarr, from_index) {
if (typeof from_index === 'undefined') {
from_index = 0;
}
var i, found, j;
for (i = from_index; i < 1 + (arr.length - subarr.length); ++i) {
found = true;
for (j = 0; j < subarr.length; ++j) {
if (arr[i + j] !== subarr[j]) {
found = false;
break;
}
}
if (found) return i;
}
return -1;
};
这些是我的测试及其预期值:
console.log(find_csa([1, 2, 3, 4, 5], [2, 3, 4]) === 1);
console.log(find_csa([1, 2, 3, 4, 5], [5]) === 4);
console.log(find_csa([1, 2, 3, 4, 5], [1, 3]) === -1);
console.log(find_csa([1, 2, 3, 4, 5], [42]) === -1);
console.log(find_csa([1, 2, 3, 4, 5], []) === 0);
console.log(find_csa([3, 4, 3, 4, 3, 4], [3, 4, 3], 1) === 2);
console.log(find_csa([6, 6, 6, 7], [6, 6, 7]) === 1);
console.log(find_csa([12, 9, 16, 42, 7, 866, 3], [16, 42, 7, 866]) === 2);
我的代码通过了测试,但正如您所看到的,它在内部循环中使用了一个布尔值found
,这只是我从嵌套循环继续外部循环的混乱方式。有更清洁的写作方式吗?我查看了Array.prototype.findIndex
,但目前这是一项实验性技术,所以我无法使用它。我想要一种适用于大多数浏览器的方法。我知道在Mozilla页面上有一个“polyfill”代码片段,但是它比我当前的代码更长,并且由于函数调用它会更慢,所以我宁愿避免它。
我这个功能的主要目标是性能(子阵列非常小,所以我相信使用Boyer-Moore string search algorithm或tries有点过头了),然后我的第二个目标是实现优雅。考虑到这两个目标,我想知道是否有更好的方法来编写此代码,或者是否有任何我缺少的JavaScript特性或函数可以帮助我避免使用found
布尔值。
JSFiddle是否可以帮助任何人:http://jsfiddle.net/qc4zxq2p/
答案 0 :(得分:12)
我是否缺少任何可以帮助我避免
的JavaScript功能或功能found
布尔值
是的,您可以在外循环中使用label:
function find_csa(arr, subarr, from_index) {
var i = from_index >>> 0,
sl = subarr.length,
l = arr.length + 1 - sl;
loop: for (; i<l; i++) {
for (var j=0; j<sl; j++)
if (arr[i+j] !== subarr[j])
continue loop;
return i;
}
return -1;
}
答案 1 :(得分:7)
这与你的相同,只是美化了一点(至少对我的美学而言):
var find_csa = function (arr, subarr, from_index) {
from_index = from_index || 0;
var i, found, j;
var last_check_index = arr.length - subarr.length;
var subarr_length = subarr.length;
position_loop:
for (i = from_index; i <= last_check_index; ++i) {
for (j = 0; j < subarr_length; ++j) {
if (arr[i + j] !== subarr[j]) {
continue position_loop;
}
}
return i;
}
return -1;
};
答案 2 :(得分:4)
使用数组方法every
:
if(subarr.every(function(e, j) { return (e === arr[i + j]); })
return i;
或(ES6提案):
if(subarr.every( (e, j) => (e === arr[i + j]) ))
return i;
但这可能只是一个好奇心或教育的例子,除非你不关心表现。
答案 3 :(得分:1)
在循环中,您可以删除found
变量,并避免像这样继续:
for (j = 0; j < subarr.length; ++j) {
if (arr[i + j] !== subarr[j]) break;
}
/*
* the above loop breaks in two cases:
* normally: j === subarr.length
* prematurely: array items did not match
* we are interested in kowing if loop terminated normally
*/
if (j === subarr.length) return i;
话虽如此,这是我使用Array.join和String.indexOf的解决方案。这只适用于数字数组:
function find_csa(arr, subarr, from_index) {
from_index |= 0;
if (subarr.length === 0) {
return from_index;
}
var haystack = "," + arr.slice(from_index).join(",") + ",",
needle = "," + subarr.join(",") + ",",
pos = haystack.indexOf(needle);
if (pos > 0) {
pos = haystack.substring(1, pos).split(",").length + from_index;
}
return pos;
}
console.log("All tests should return true");
console.log(find_csa([1, 2, 3, 4, 5], [1, 2, 3]) === 0);
console.log(find_csa([1, 2, 3, 4, 5], [2, 3, 4]) === 1);
console.log(find_csa([1, 2, 3, 4, 5], [5]) === 4);
console.log(find_csa([1, 2, 3, 4, 5], [6]) === -1);
console.log(find_csa([1, 2, 3, 4, 5], [1, 3]) === -1);
console.log(find_csa([6, 6, 6, 7], [6, 6, 7]) === 1);
console.log(find_csa([1, 2, 3, 4, 5], []) === 0);
console.log(find_csa([3, 4, 3, 4, 3, 4], [3, 4, 3], 1) === 2);
console.log(find_csa([1, 2, 3, 4, 5], [], 1) === 1);
答案 4 :(得分:1)
阅读基于zerkms命题的初步讨论,我有兴趣尝试使用JSON.stringify的解决方案,尽管有不利的意见。
然后我终于得到了一个解决方案,它正确地通过了所有测试 可能不是更快的方法,但肯定是最短的方法:
var find_csa = function (arr, subarr, from_index) {
var start=from_index|0,
needle=JSON.stringify(subarr),
matches=JSON.stringify(arr.slice(start)).
match(new RegExp('^\\[(.*?),?'+
needle.substr(1,needle.length-2).replace(/([\[\]])/g,'\\$1')
));
return !!matches?(matches[1].length?matches[1].split(',').length:0)+start:-1;
}
上面的代码接受Shashank建议的数组数组,但无法处理包含逗号的项目。
所以我开发了另一个也接受逗号的解决方案(感谢Steven Levithan关于while(str!=(str=str.replace(regexp,replacement)));
关于var find_csa = function (arr, subarr, from_index) {
var start=from_index|0,
commas=new RegExp('(?:(\')([^,\']+),([^\']+)\'|(")([^,"]+),([^"]+))"'),
strip_commas='$1$2$3$1$4$5$6$4',
haystack=JSON.stringify(arr.slice(start)),
needle=JSON.stringify(subarr).replace(/^\[(.*)\]$/,'$1');
while(haystack!=(haystack=haystack.replace(commas,strip_commas)));
while(needle!=(needle=needle.replace(commas,strip_commas)));
matches=haystack.match(new RegExp('^\\[(.*?),?'+needle.replace(/([\[\]])/g,'\\$1')));
return !!matches?(matches[1].length?matches[1].split(',').length:0)+start:-1;
}
)。
但这只是为了好玩,因为:
无论如何,这是它:
{{1}}
答案 5 :(得分:0)
我用这个代码解决了这个问题:
getCount(arr)
{
const chunked = [];
for(let i=0; i<arr.length; i++) chunked[i] = [];
let sub = 0;
for (let i = 1; i < arr.length; i++) {
if (arr[i]>arr[i-1]) {
chunked[sub].push(...[arr[i-1],arr[i]]);
} else {
sub++
}
}
const chunked2 = [...chunked.filter(k=>k.length).map(k => [...new Set(k)])];
for (let i = 0; i < chunked2.length; i++) {
if (chunked2[i+1])
if( chunked2[i][chunked2[i].length - 1] > chunked2[i+1][0]) {
chunked2[i+1].shift();
}
}
return [].concat.apply([], chunked2).lenght;
}