【问题标题】:Lodash group by multiple properties if property value is true如果属性值为 true,则 Lodash 按多个属性分组
【发布时间】:2017-03-01 13:58:30
【问题描述】:

我有一系列需要按品牌和型号分组的车辆,前提是“已选择”属性为真。生成的对象应包含制作模型和计数的属性。使用 lodash,我如何将车辆对象组织成所需的结果对象。我能够获取按 makeCode 分组的车辆对象,但我不确定如何按多个属性进行分组。

按使代码有效的方式分组

      var vehicles = _.groupBy(response.vehicleTypes, function(item)
      {
        return item.makeCode; // how to group by model code as well
      });

初始车辆

{
    id: 1, 
    selected: true, 
    makeCode: "Make-A", 
    modelCode: "Model-a", 
    trimCode: "trim-a", 
    yearCode: "2012"
},
{
    id: 2, 
    selected: false, 
    makeCode: "Make-A", 
    modelCode: "Model-a", 
    trimCode: "trim-a", 
    yearCode: "2013"
},
{
    id: 3, 
    selected: true, 
    makeCode: "Make-B", 
    modelCode: "Model-c", 
    trimCode: "trim-a", 
    yearCode: "2014"
},
{
    id: 25, 
    selected: true, 
    makeCode: "Make-C", 
    modelCode: "Model-b", 
    trimCode: "trim-b", 
    yearCode: "2012"
},
{
    id: 26, 
    selected: true, 
    makeCode: "Make-C", 
    modelCode: "Model-b", 
    trimCode: "trim-a", 
    yearCode: "2013"
}

结果对象

{
    Make-A: {
        Model-a: {
            count: 1
        }
    }
},

{
    Make-B: {
        Model-c: {
            count: 1
        }
    }
},
{
    Make-C: {
        Model-b: {
            count: 2
        }
    }
}

【问题讨论】:

  • Model-X 和 Model-Y 有 Make -T 时怎么样

标签: javascript lodash


【解决方案1】:

由于您已经在使用 lodash,您可以利用 _.filter 函数。这将仅返回 selected 为 true 的项目。

var selectedVehicles = _.filter(response.vehicleTypes, 'selected');

现在您有了selectedVehicles 数组,您可以使用原始代码按makeCode 进行分组。

selectedVehicles = _.groupBy(selectedVehicles, function(item) {
  return item.makeCode;
});

这会返回一个对象,因此我们需要遍历这些键,并执行我们的第二个groupBy

_.forEach(selectedVehicles, function(value, key) {
  selectedVehicles[key] = _.groupBy(selectedVehicles[key], function(item) {
    return item.modelCode;
  });
});

从这里你将有一个表单对象。我会留给你从每个数组中获取计数。

{ 'Make-A': { 'Model-a': [ ... ] },
  'Make-B': { 'Model-c': [ ... ] },
  'Make-C': { 'Model-b': [ ..., ... ] } }

【讨论】:

  • 感谢您逐步完成,非常有意义!我想多了。
  • 你可以使用property的简写,然后_.groupBy(selectedVehicles, 'makeCode')
【解决方案2】:

你可以使用Array.prototype.reduce(),一次循环O(n):

var arr = [{"id":1,"selected":true,"makeCode":"Make-A","modelCode":"Model-a","trimCode":"trim-a","yearCode":"2012"},{"id":2,"selected":false,"makeCode":"Make-A","modelCode":"Model-a","trimCode":"trim-a","yearCode":"2013"},{"id":3,"selected":true,"makeCode":"Make-B","modelCode":"Model-c","trimCode":"trim-a","yearCode":"2014"},{"id":25,"selected":true,"makeCode":"Make-C","modelCode":"Model-b","trimCode":"trim-b","yearCode":"2012"},{"id":26,"selected":true,"makeCode":"Make-C","modelCode":"Model-b","trimCode":"trim-a","yearCode":"2013"},{"id":29,"selected":false,"makeCode":"Make-A","modelCode":"Model-g","trimCode":"trim-a","yearCode":"2013"},{"id":2,"selected":true,"makeCode":"Make-A","modelCode":"Model-h","trimCode":"trim-a","yearCode":"2013"}];

var result = arr.reduce(function(map, obj) {
  if(!obj.selected) {
    return map;
  }
  
  var makeCode = map[obj.makeCode] = map[obj.makeCode] || {};
  
  var modelCode = makeCode[obj.modelCode] = makeCode[obj.modelCode] || { count: 0 };
  
  modelCode.count++;
  
  return map;
}, Object.create(null));

console.log(result);

使用解构的更易读、更现代的版本:

const arr = [{"id":1,"selected":true,"makeCode":"Make-A","modelCode":"Model-a","trimCode":"trim-a","yearCode":"2012"},{"id":2,"selected":false,"makeCode":"Make-A","modelCode":"Model-a","trimCode":"trim-a","yearCode":"2013"},{"id":3,"selected":true,"makeCode":"Make-B","modelCode":"Model-c","trimCode":"trim-a","yearCode":"2014"},{"id":25,"selected":true,"makeCode":"Make-C","modelCode":"Model-b","trimCode":"trim-b","yearCode":"2012"},{"id":26,"selected":true,"makeCode":"Make-C","modelCode":"Model-b","trimCode":"trim-a","yearCode":"2013"},{"id":29,"selected":false,"makeCode":"Make-A","modelCode":"Model-g","trimCode":"trim-a","yearCode":"2013"},{"id":2,"selected":true,"makeCode":"Make-A","modelCode":"Model-h","trimCode":"trim-a","yearCode":"2013"}];

const result = arr.reduce((acc, { selected, makeCode, modelCode }) => {
  if(!selected) return acc;
  
  if(!acc[makeCode]) acc[makeCode] = {};
  
  const make = acc[makeCode];
  
  if(!make[modelCode]) make[modelCode] = { count: 0 };
  
  make[modelCode].count++;
  
  return acc;
}, Object.create(null));

console.log(result);

【讨论】:

  • 这个语句有什么作用? var makeCode = map[obj.makeCode] = map[obj.makeCode] || {};
  • 我这个var makeCode = map[obj.makeCode] = map[obj.makeCode] || {}; 中的小弟的主要想法是-将map[obj.makeCode] 或空对象{} 分配给map[obj.makeCode](它用作备用map[obj.makeCode] 不存在然而),并将map[obj.makeCode] 分配给makeCode。我必须承认,它不是世界上最易读的代码。当我遇到他时,我会告诉我年轻的自己:)
【解决方案3】:

我不确定这是否能解决您的问题,但在 group_by 中您可以添加自定义逻辑,允许您创建复合键。

请记住,SEPERATOR 值应根据您使用的数据源进行定义,如果模型或类型中出现“--”字符,则不应使用它们,因为它不允许您反转分组过程。

const SEPERATOR = "--";
_.chain(data).filter((item) => item.selected).groupBy((item)=>`${item.model}${SEPERATOR}${item.type}`).value();

【讨论】:

  • 这种方法效率更高,因为我们只循环遍历项目一次。
  • 天才!只需在之后添加.value()
  • 不错,这是个好主意。注意小边缘情况。假设 '-' 是一个有效字符。这个对象{model:'---', type:'-'} 和这个对象{model:'--',type:'--'} 将被分组,尽管它们没有相同的模型和类型
  • 嗨@yonBav 你在这里有一点,应该使用一个常量变量来保持分隔符值,这对于反向分组过程或从字典键访问模型或类型信息也很重要.
  • 但是@yonBav我不同意你的例子,分隔符的值必须根据你处理的数据来选择。示例 sn-p 只是显示如何解决问题。未考虑数据上下文。
【解决方案4】:

const result = _.chain(vehicles)
.filter('selected')
.groupBy('makeCode')
.mapValues(values => _.chain(values)
    .groupBy('modelCode')
    .mapValues(_.size)
    .value()
)
.value()

【讨论】:

    【解决方案5】:

    const multiGroupBy = (array, group, ...restGroups) => {
      if(!group) {
        return array;
      }
      const currGrouping = _.groupBy(array, group);
      if(!restGroups.length) {
        return currGrouping;
      }
      return _.transform(currGrouping, (result, value, key) => {
        result[key] = multiGroupBy(value, ...restGroups);
      }, {});
    };
    
    console.log(multiGroupBy([{x:1,y:1,z:1},{x:1,y:2,z:1},{x:2,y:1,z:1},{x:2,y:2,z:1},{x:1,y:1,z:2},{x:1,y:2,z:2},{x:2,y:1,z:2},{x:2,y:2,z:2}],'x','y'));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>

    或者如果您更喜欢旧语法

    function multiGroupBy(array, group) {
      if(!group) {
        return array;
      }
      var currGrouping = _.groupBy(array, group);
      var restGroups = Array.prototype.slice.call(arguments);
      restGroups.splice(0,2);
      if(!restGroups.length) {
        return currGrouping;
      }
      return _.transform(currGrouping, function(result, value, key) {
        result[key] = multiGroupBy.apply(null, [value].concat(restGroups));
      }, {});
    }
    
    console.log(multiGroupBy([{x:1,y:1,z:1},{x:1,y:2,z:1},{x:2,y:1,z:1},{x:2,y:2,z:1},{x:1,y:1,z:2},{x:1,y:2,z:2},{x:2,y:1,z:2},{x:2,y:2,z:2}],'x','y'));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>

    【讨论】:

      【解决方案6】:

      在纯 JS 上对没有 LODASH 的对象或数组进行分组和过滤:

      // Группировка и фильтрация объекта или массива без LODASH на чистом JS:
      let k = "HUID", // group by; 
      input = [
      {HUID:11,test:1},
      {HUID:11,test:111},
      {HUID:'eeeeeeeeeeee',test:11111},
      {HUID:22,test:2},
      {HUID:33,test:3}
      ],
      result = input.reduce((map, obj) => { 
      //if(!obj.selected) { return map; } 
      let makeCode = (map[obj[k]] = map[obj[k]] || {}); // var modelCode = makeCode[obj.HUID] = makeCode[obj.HUID] || { count: 0 }; 
      let l = map[obj[k]],
          m = Object.keys(l).length; 
      l[m] = { ...obj }; 
      return map; 
      }, {} );
      console.log(result);

      Copied from VK

      【讨论】:

        【解决方案7】:

        你不需要重复所有的数组对象。使用 loadash,您可以简化如下:

        _.mapValues(_.groupBy(object, 'yourKeys'))
        

        【讨论】:

          【解决方案8】:

          如果您关注结果,则以下代码有效:

          在我的例子中,BrandItem Code 是属性

          const products = _.groupBy(this.productsTable.data, (item) => {
              return [item['Brand'], item['Item Code']];
          });
          

          【讨论】:

          • 好答案。如果您再多花 10 秒时间来真正注意到他要求的输出格式是不同的:)
          • 并不是每个人都会遇到与问题中提到的完全相同的问题。这个答案是针对这些情况的。 @Dheeraj
          【解决方案9】:

          对于那些您看起来与所有者相似但没有布尔值作为过滤器作为第一个属性的人,您可以执行双重 groupby:

          export function groupByLodash(results: myType[]) {
            return mapValues(
              groupBy(results, 'property.to.group'),
              (result: myType) => groupBy(result, 'second.property.to.group'),
            );
          }
          

          结果应该是这样的:

          {
          "property.to.group": { "second.property.to.group": {...result}}
          }
          

          【讨论】:

          • 编辑@Elikill58
          • 好的,完美! :)
          猜你喜欢
          • 2016-03-18
          • 2019-07-29
          • 2019-08-12
          • 2018-08-28
          • 1970-01-01
          • 2020-04-27
          • 1970-01-01
          • 1970-01-01
          • 2020-09-05
          相关资源
          最近更新 更多