链接数组方法(filter、map、reduce)而不是使用双循环

Posted

技术标签:

【中文标题】链接数组方法(filter、map、reduce)而不是使用双循环【英文标题】:Chaining array methods (filter, map, reduce) instead of using double loop 【发布时间】:2020-11-25 06:34:23 【问题描述】:

我遇到了一个我无法解决的问题...... 所以这就是我想要做的。

给定以下对象数组,

products = [
     name: 'Sonoma', ingredients: ['artichoke', 'sundried tomatoes', 'mushrooms'], containsNuts: false ,
     name: 'Pizza Primavera', ingredients: ['roma', 'sundried tomatoes', 'goats cheese', 'rosemary'], containsNuts: false ,
     name: 'South Of The Border', ingredients: ['black beans', 'jalapenos', 'mushrooms'], containsNuts: false ,
     name: 'Blue Moon', ingredients: ['blue cheese', 'garlic', 'walnuts'], containsNuts: true ,
     name: 'Taste Of Athens', ingredients: ['spinach', 'kalamata olives', 'sesame seeds'], containsNuts: true ,
];

我知道我可以通过嵌套循环运行它以按成分名称添加键,然后在循环计数时递增值,如下所示:

      let ingredientCount = ; 

  for (i = 0; i < products.length; i += 1) 
    for (j = 0; j < products[i].ingredients.length; j += 1)  //loop ingredients 
      ingredientCount[products[i].ingredients[j]] = (ingredientCount[products[i].ingredients[j]] || 0) + 1; 
    
  

因此,componentCount 应该类似于: "artichoke": 1 "mushrooms": 2 ***

这里的问题是我需要使用 map 和 reduce 来创建与上面相同的结果。

let ingredientCount = 

    ingredientCount = 
    products.filter ((value) => 
        // filter out arrays within ingredients 
        // so out come should be like  
        /* 
        [ingredients: ['artichoke', 'sundried tomatoes', 'mushrooms']
        ,ingredients: ['roma', 'sundried tomatoes', 'goats cheese', 'rosemary']
        ,ingredients: ['black beans', 'jalapenos', 'mushrooms']
        ,ingredients: ['blue cheese', 'garlic', 'walnuts']
        ,ingredients: ['spinach', 'kalamata olives', 'sesame seeds']
        */

    ).map ((value) => 
        /* then take out ingredients and map this array to  
        arthichoke: ['artichoke','artichoke','artichoke']
        sundried tomatoes: ['sundried tomatoes']
        etc... 
        */

    
    ).reduce((acc, value) => 
        /* then reduce arrays within each key to numbers. 
        hence, the output should be 

        artichokes: artichokes.length (i.e. 3 )
        sundried toamatoes: 1
        
        */
    )

无论如何我可以使用上述数组方法获得完全相同的结果而不必使用循环吗?

提前致谢。

【问题讨论】:

我认为循环很好。但是您只想使用数组方法。请你解释一下过滤器部分,因为我不明白 我不认为这种连接能更好地解决问题,因为两个主要原因,第一它混淆了代码,第二性能最差。另一方面,循环示例更具可读性和更快 【参考方案1】:

您需要使用map()flat(),然后是reduce()。函数flat() 将数组展平。

products = [
     name: 'Sonoma', ingredients: ['artichoke', 'sundried tomatoes', 'mushrooms'], containsNuts: false ,
     name: 'Pizza Primavera', ingredients: ['roma', 'sundried tomatoes', 'goats cheese', 'rosemary'], containsNuts: false ,
     name: 'South Of The Border', ingredients: ['black beans', 'jalapenos', 'mushrooms'], containsNuts: false ,
     name: 'Blue Moon', ingredients: ['blue cheese', 'garlic', 'walnuts'], containsNuts: true ,
     name: 'Taste Of Athens', ingredients: ['spinach', 'kalamata olives', 'sesame seeds'], containsNuts: true ,
];

let obj = products
              .map(p => p.ingredients)
              .flat()
              .reduce((obj, val) => 
                  obj[val] = (obj[val] || 0) + 1;
                  return obj;
              , );
console.log(obj);

【讨论】:

【参考方案2】:

您可以使用两个forEachmap,并维护一个只需要更新的最终数组。

let products = [
     name: 'Sonoma', ingredients: ['artichoke', 'sundried tomatoes', 'mushrooms'], containsNuts: false ,
     name: 'Pizza Primavera', ingredients: ['roma', 'sundried tomatoes', 'goats cheese', 'rosemary'], containsNuts: false ,
     name: 'South Of The Border', ingredients: ['black beans', 'jalapenos', 'mushrooms'], containsNuts: false ,
     name: 'Blue Moon', ingredients: ['blue cheese', 'garlic', 'walnuts'], containsNuts: true ,
     name: 'Taste Of Athens', ingredients: ['spinach', 'kalamata olives', 'sesame seeds'], containsNuts: true ,
];

let iCount = ;
products.forEach((c) => c.ingredients.forEach((i) => iCount.hasOwnProperty(i) ? iCount[i]++ : iCount[i] = 1));
console.log(iCount);

【讨论】:

你使用的是map(),就像forEach()一样。【参考方案3】:

使用数组的 .reduce 方法和 .forEach 方法的解决方案。

var products = [
     name: 'Sonoma', ingredients: ['artichoke', 'sundried tomatoes', 'mushrooms'], containsNuts: false ,
     name: 'Pizza Primavera', ingredients: ['roma', 'sundried tomatoes', 'goats cheese', 'rosemary'], containsNuts: false ,
     name: 'South Of The Border', ingredients: ['black beans', 'jalapenos', 'mushrooms'], containsNuts: false ,
     name: 'Blue Moon', ingredients: ['blue cheese', 'garlic', 'walnuts'], containsNuts: true ,
     name: 'Taste Of Athens', ingredients: ['spinach', 'kalamata olives', 'sesame seeds'], containsNuts: true ,
];

var result = products.reduce((acc,obj) => 
   obj.ingredients.forEach(ob=> acc[ob] = acc[ob]+1 || 1)
    return acc;
,);

console.log(result);

【讨论】:

【参考方案4】:

使用Array.prototype.reduce 来减少数组并随时增加计数。

const products = [
    name: 'Sonoma',
    ingredients: ['artichoke', 'sundried tomatoes', 'mushrooms'],
    containsNuts: false
  ,
  
    name: 'Pizza Primavera',
    ingredients: ['roma', 'sundried tomatoes', 'goats cheese', 'rosemary'],
    containsNuts: false
  ,
  
    name: 'South Of The Border',
    ingredients: ['black beans', 'jalapenos', 'mushrooms'],
    containsNuts: false
  ,
  
    name: 'Blue Moon',
    ingredients: ['blue cheese', 'garlic', 'walnuts'],
    containsNuts: true
  ,
  
    name: 'Taste Of Athens',
    ingredients: ['spinach', 'kalamata olives', 'sesame seeds'],
    containsNuts: true
  ,
];

const result = products.reduce((acc,  ingredients ) => 
  ingredients.forEach((ingredient) => 
    acc[ingredient] = (acc[ingredient] || 0) + 1;
  );
  return acc;
, Object.create(null));

console.log(result);

【讨论】:

【参考方案5】:

使用flatMapreduce。 (除了??, 运算符)

const products = [
     name: 'Sonoma', ingredients: ['artichoke', 'sundried tomatoes', 'mushrooms'], containsNuts: false ,
     name: 'Pizza Primavera', ingredients: ['roma', 'sundried tomatoes', 'goats cheese', 'rosemary'], containsNuts: false ,
     name: 'South Of The Border', ingredients: ['black beans', 'jalapenos', 'mushrooms'], containsNuts: false ,
     name: 'Blue Moon', ingredients: ['blue cheese', 'garlic', 'walnuts'], containsNuts: true ,
     name: 'Taste Of Athens', ingredients: ['spinach', 'kalamata olives', 'sesame seeds'], containsNuts: true ,
];

const ingredientCount = products
  .flatMap(( ingredients ) => ingredients)
  .reduce((acc, item) => ((acc[item] = (acc[item] ?? 0) + 1), acc), );

console.log(ingredientCount);

【讨论】:

以上是关于链接数组方法(filter、map、reduce)而不是使用双循环的主要内容,如果未能解决你的问题,请参考以下文章

map、foreach、reduce、filters的用法及区别

filter,map,reduce三个数组高阶函数的使用

Swift中 Map,Flatmap,Filter,Reduce的用法

js中map,forEach,filter,reduce等方法是ECMAScript5 Array新增方法

js数组中indexOf/filter/forEach/map/reduce详解

ES6 数组函数forEach()map()filter()find()every()some()reduce()