javascript(ES6):比“for循环”更有效的方法吗?
Posted
技术标签:
【中文标题】javascript(ES6):比“for循环”更有效的方法吗?【英文标题】:javascript (ES6): any more efficient way than "for loops"? 【发布时间】:2019-03-21 13:37:03 【问题描述】:这不是重复的。请在下面查看我的评论!
有人知道比 ES6 中的 for 循环 更有效的解决方案吗?
我写了以下,缺乏性能。有什么改进的想法吗?高度赞赏。
基本上我有一个关于汽车的对象和一个关于用户偏好的数组。预期的行为是将所有相关的汽车名称推送到一个数组中。
用户可以给出任意数量的偏好。如果偏好中提到了所有规格,则应仅推送汽车名称。因此,一些偏好将是“剩菜”。
出于这个原因,在以下示例中出现了本田,但没有出现宝马,这是预期的(但行为非常缓慢)。
// Car objects
const cars = [
name: "Honda",
category: "eco",
specs:
0: "green",
1: "fast",
2: "automatic"
,
name: "BMW",
category: "sport",
specs:
0: "blue",
1: "fast",
2: "automatic"
]
// User preferences
const preferences = ["green", "fast", "4x4", "automatic", "panorama"]
// function to get length/amount of car specifications
function objsize(Myobj)
var osize = 0,
key;
for (key in Myobj)
if (Myobj.hasOwnProperty(key)) osize++;
return Object(osize);
;
//function to check if ALL specifications are included in the user preferences
function checkSpecs(spec_item)
return preferences.includes(spec_item)
// main function
function filter_func()
//final results
let matched_cars = []
for (i = 0; i < objsize(cars); i++)
let specs_collector = []
for (j = 0; j < objsize(cars[i].specs); j++)
specs_collector.push(cars[i].specs[j])
if (specs_collector.every(checkSpecs) === true)
matched_cars.push(cars[i].name)
specs_collector = []
console.log(matched_cars)
filter_func()
【问题讨论】:
Check if every element in one array is in a second array的可能重复 @HereticMonkey 请删除“可能重复的标签”。最初的标题名称是关于 *** 的另一个问题。我正在寻找一种比 for 循环更有效的方法。建议的解决方案使用 for 循环。 “我写了以下,性能不足。” 性能如何?你是怎么测量的?每当您有一个需要处理的集合值时,您都必须使用某种循环来处理每个值,无论是显式的还是隐式的。 FWIW,objsize
是不必要和错误的。 Object(osize)
创建一个 Number 对象,这意味着 5 === Object(5)
是 false
。要遍历数组,请使用for(var i = 0; i < array.length; i++)
或for (var item of arr)
或arr.forEach(function(item) )
)。要遍历对象,请使用for (var prop in obj)
。但看起来cars[i].specs
应该是一个数组,而不是一个对象。
“所需的解决方案是没有任何设计上很慢的“for 循环”。” “设计上”它们并不慢。什么让你有那个想法? for
循环在浏览器中进行了大量优化。当然,循环可能不是解决问题的“最佳”解决方案,但这是一个不同的问题。
【参考方案1】:
没有办法逃脱至少一个循环。您总是必须遍历所有汽车,无论是使用 for... 还是使用其他构造(如 array.filter())。但是还有另一种获得性能的方法。您可以使用位掩码。这将需要更改汽车对象的数据结构,以便每辆汽车已经包含与其规格相对应的位掩码,并且当用户选择所需规格时,同样应添加规格代码。 (但是,我怀疑这可能会带来太多的麻烦而收效甚微。)
// Let's pretend there are preset binary digits corresponding
// to each one of the available preferences:
//
// "blue" => 1
// "green" => 2
// "red" => 4
// "fast" => 8
// "slow" => 16
// "automatic" => 32
// "4x4" => 64
// "panorama" => 128
//
// You would encode this into the data before processing
var cars = [
name: "Honda",
category: "eco",
specs: ["green", "fast", "automatic"],
bin_specs: 42 // 2 + 8 + 32
,
name: "BMW",
category: "sport",
specs: ["blue", "fast", "automatic"],
bin_specs: 41 // 1 + 8 + 32
]
const preferences = ["green", "fast", "4x4", "automatic", "panorama"]
const bin_preferences = 234 // 2 + 8 + 64 + 32 + 128]
let filtered = cars.filter(car => (car.bin_specs & bin_preferences) === car.bin_specs)
console.log(filtered);
【讨论】:
这是一个非常有趣的答案,但只有当我知道所有可能的用户偏好元素时才有可能。在我的情况下,ist 不起作用,但肯定适用于其他用例。谢谢你。 是的,您必须将这些值预先分配给每个首选项如果您无法控制后端,那就别管它了。【参考方案2】:-- 编辑--
使用 OP 中的数据:
const array_intersect = (a, b) => a.filter( i => (b.indexOf(i) >= 0) )
const a_contains_b = (a, b) => array_intersect(a, b).length == b.length
var cars = [
name: "Honda",
category: "eco",
specs: ["green", "fast", "automatic"]
,
name: "BMW",
category: "sport",
specs: ["blue", "fast", "automatic"]
]
const preferences = ["green", "fast", "4x4", "automatic", "panorama"]
let filtered = cars.filter(car => a_contains_b(preferences, car.specs))
console.log(filtered);
【讨论】:
虽然您没有for
循环,但您的array_intersect
函数是O(n^2),因此效率不高。
@slider 原则上,你是对的。但是唯一知道的方法是在类似的条件下进行测试,这将被使用,因为测试表明存在大相径庭的结果。检查github.com/dg92/Performance-Analysis-JS【参考方案3】:
您无法真正避免查看每辆车,也无法避免查看汽车的每个规格,因为您想测试其中的每一个。您可以使用Set 避免每次都循环使用首选项。
所以这可能更快也可能不会更快,但它要简单得多更容易理解,因为代码几乎读起来像英语:filter cars where each spec is in the preferences:
// Car objects
const cars = [
name: "Honda",
category: "eco",
specs: ["green", "fast","automatic"]
,
name: "BMW",
category: "sport",
specs: ["blue", "fast","automatic"]
]
const preferences = new Set(["green", "fast", "4x4", "automatic", "panorama"])
let filtered = cars.filter(car => car.specs.every(spec => preferences.has(spec)))
console.log(filtered)
【讨论】:
"...这可能会也可能不会更快" 由于Set
,它比原来的速度快preferences.length
倍。
@slider,是的……但有时像 every
这样的 es6 函数在实践中的性能不如简单的 for
循环。我想我提到这一点是因为我不想做出任何我没有测试过的声明。
@MarkMeyer 你的 sn-p 既漂亮又快。惊人的。我刚刚在我的代码中尝试了它,它就像一个魅力,它“感觉”确实更快。我将尝试衡量我的解决方案并将其与您的解决方案进行比较。之后我会发布结果。谢谢!以上是关于javascript(ES6):比“for循环”更有效的方法吗?的主要内容,如果未能解决你的问题,请参考以下文章
[ES6深度解析]2:迭代器(Iterators )和for of循环