我写了一段运行正常的代码。我想要一个新的数组,该数组由来自myArr的元素按orderArr中指定的顺序组成。但是,它在另一个for循环中使用for循环来匹配数组元素。
var myArr = ['a', 'b', 'c', 'd', 'e'];
var orderArr = ['e', 'c'];
var reArr = [];
for (var i = 0; i < orderArr.length; i++) {
for (var k = 0; k < myArr.length; k++) {
if (myArr[k] == orderArr[i]) {
reArr.push(myArr[k]);
}
}
}
console.log(reArr);
我经常听说在另一个for循环中使用for循环是不好的做法,即使每个都应该避免。
我怎样才能重新编写此代码。
答案 0 :(得分:4)
在您的案例中嵌套循环的复杂性为O(n * m) - n为orderArr
的长度,m为myArr
的长度。
此解决方案复杂度为O(n + m),因为我们使用复杂度为O(m)的Array#reduce创建字典对象,然后使用filtering orderArray
创建字典对象O(n)的复杂性。
注1:对于小型数组,这应该不重要。因此,除非你在两个数组中都有几千个,否则你可以继续使用嵌套循环。
注2:此代码假定myArr
中没有重复项。如果存在重复项,则结果将与嵌套循环的结果不同。
var myArr = ['a', 'b', 'c', 'd', 'e'];
var orderArr = ['e', 'c'];
var myArrDict = myArr.reduce(function(r, s) {
r[s] = true;
return r;
}, Object.create(null));
var reArr = orderArr.filter(function(s) {
return this[s];
}, myArrDict);
console.log(reArr);
答案 1 :(得分:4)
我不一定会说在循环中使用循环是一种不好的做法 - 实际上Ori Drori打败了我,说明这种做法的效率可能只取决于数据集的大小。
例如,sorting algorithms有很多实现,你经常会在循环中找到循环。但是,随着数据大小的变化,实施细节会影响性能。
我个人的经验是,当我看到嵌套循环时,我立即问自己,它所执行的操作是否需要使用不同的算法进行优化。通常情况下,在JavaScript(浏览器)应用程序中,答案是&#34; no&#34;因为数据大小很小,不足以产生有影响力的差异。
答案 2 :(得分:1)
就个人而言,我喜欢为此特定场景设置专用的关联功能。
"use strict";
function correlate(
outer,
inner,
outerKeyOrKeySelector = x => x,
innerKeyOrKeySelector = x => x
) {
const outerValues = [...outer];
const innerValues = [...inner];
const outerToKey = typeof outerKeyOrKeySelector === 'function'
? outerKeyOrKeySelector
: (x => x[outerKeyOrKeySelector]);
const innerToKey = typeof innerKeyOrKeySelector === 'function'
? innerKeyOrKeySelector
: (x => x[innerKeyOrKeySelector]);
const outerKeyed = outerValues.map(value => ({key: outerToKey(value), value});
const innerKeyed = innerValues.map(value => ({key: innerToKey(value), value});
return outerKeyed.reduce((results, {key: oKey, value: oValue}) => [
...results,
...innerKeyed
.filter(({key: iKey}) => oKey === iKey)
.map(({value: iValue}) => [oValue, iValue])
], []);
}
这基本上就像一个JOIN,允许你根据属性值或任意投影函数关联两个数组。
看起来有点矫枉过正,而YMMV,但我发现它非常有用。
应用于手头的问题:
reArr = correlate(orderArr, myArr).map(([first, second]) => first);
但它可以轻松处理复杂的场景
reArr = correlate(orders, customers, o => o.customer.name, c => c.name)
.map(([first, second]) => {order: first, customer: second})
.forEach(({customer, order}) => {
customer.orders = [...customer.orders, order];
});
答案 3 :(得分:1)
为了补充这里的一些好答案(尤其是Ori&#39; s),我认为两个非常大的数组的最佳解决方案是合并连接(这是关系数据库处理案例的方式。这里的想法我们只是在比较我们所知道的大致相同的值。
第一步是对两个数组(O(n log n)+ O(m log m))进行排序,然后比较每个数组中的第一个值。有三种可能性:
LinkGeneratorInterface
...并重复这个过程,直到其中一个列表耗尽...
A<B
We discard A and fetch the next value from the same list A came from
A=B
We add A to the keep list and retrieve the next value from that array
A>B
We discard B and retrieve the next value from its list
答案 4 :(得分:0)
我认为您的代码没有任何问题,但如果您真的想要,可以使用此功能消除(对您可见)循环:
var reArr = myArr.filter((n) => orderArr.includes(n));
(在旧版本的IE中不起作用)。
答案 5 :(得分:0)
这是一个非常简单的ES5版本 - 如果您的值不是对象,则内部循环避免: -
var myArr = ['a', 'b', 'c', 'd', 'e'];
var orderArr = ['e', 'c'];
var myArrIndex = {};
for(var i=0;i < myArr .length; i++){
myArrIndex[myArr[i]]=myArr[i];
};
for(var i=0;i < orderArr.length; i++){
var orderArrayItem = orderArr[i];
if (myArrIndex[orderArrayItem]){
reArr.push(orderArrayItem);
}
}
console.log(reArr);
答案 6 :(得分:0)
将myArr
映射到对象中。我会添加一个&#39; a&#39;到myArr
和&#39; f&#39;到orderArr
。
var myArr = ['a', 'a', 'b', 'c', 'd', 'e'];
var orderArr = ['e', 'c', 'f']
var myObj = createObjectFrom(myArr);
var reArr = [];
function createObjectFrom(array) {
var obj = {};
var arrValue;
for (var i = 0; i < array.length; i++) {
arrValue = array[i];
if (obj[arrValue]) {
obj[arrValue]++;
} else {
obj[arrValue] = 1;
}
}
return obj; // { 'a': 2, 'b': 1, 'c': 1, 'd': 1, 'e': 1 }
}
var orderValue;
for(var i=0;i<orderArr.length;i++){
orderValue = orderArr[i];
if (myObj.hasOwnProperty(orderValue)) {
for (var j = 0; j < myObj[orderValue]; j++) {
reArr.push(orderValue);
}
}
}
console.log(reArr); // [ "c", "e" ]
而不是循环(6 * 3 =)18次,你只需要循环(6 + 3 =)9次。
我考虑到重复项使代码更复杂一些,因此第二个用于循环(j)。
作为一个额外的奖励,这是一个我觉得有趣的视频:
答案 7 :(得分:0)
你可以做的是组合两个数组并使用reduce
删除除重复项之外的所有内容,如下所示:
var myArr = ['a', 'b', 'c', 'd', 'e'];
var orderArr = ['e', 'c'];
// Merge the two arrays
var reArr = myArr.concat(orderArr);
let result = reArr.reduce((arr, val) => {
// return only equal values that are not already in the new array
// If the value is in the new array 'indexOf()' will be greater than -1
// we will then not modify the current array
//
// If the value is not in the new array and is equal to the current lookup
// then add it to the filter. Once filtered, we can check
// if the filter contains more than one item if it does
// then we can add it to the new array
return reArr.filter(v => v == val && arr.indexOf(val) == -1).length > 1
// If it is not in the new array add it using 'concat()'
// otherwise we will return the current array as is
? arr.concat(val) : arr
}, [])
console.log(result);
答案 8 :(得分:0)
您可以使用Map
并在数组中收集所有相同的值,以便稍后与结果集进行连接。
var array = ['a', 'b', 'c', 'd', 'e'],
order = ['e', 'c'],
map = new Map,
result;
array.forEach(v => map.set(v, (map.get(v) || []).concat(v)));
result = order.reduce((r, v) => r.concat(map.get(v)), []);
console.log(result);
&#13;