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 &lt; 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循环”更有效的方法吗?的主要内容,如果未能解决你的问题,请参考以下文章

javascript ES6 - For循环

JavaScript for 循环进化史

JavaScript 中 for 循环

[ES6深度解析]2:迭代器(Iterators )和for of循环

JavaScript的流程控制之for循环语句和while循环语句

ES6中for...of循环与其他遍历语法的比较