Группировка Lodash по нескольким свойствам, если значение свойства равно true

#javascript #lodash

#javascript #Lodash

Вопрос:

У меня есть массив транспортных средств, которые необходимо сгруппировать по марке и модели, только если свойство ‘selected’ имеет значение true. Результирующий объект должен содержать свойства для make model и count. Используя Lodash, как я могу организовать объекты vehicle в объекты желаемого результата. Я могу сгруппировать объекты vehicle по MakeCode, но я не уверен, как группировать по нескольким свойствам.

группировать по коду make работает

       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
        }
    }
}
  

Комментарии:

1. как насчет того, когда есть Make -T с Model-X и Model-Y

Ответ №1:

Я не уверен, решит ли это вашу проблему, но в group_by вы можете добавить пользовательскую логику, которая позволит вам создать составной ключ.

Помните, что значение РАЗДЕЛИТЕЛЯ должно быть определено в соответствии с источником данных, с которым вы работаете, если символы «—» встречаются в модели или типе, вы не должны их использовать, поскольку это не позволит вам отменить процесс группировки.

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

Комментарии:

1. Этот метод более эффективен, поскольку мы перебираем элементы только один раз.

2. Гениально! Просто добавьте .value() после .

3. приятно, что это хорошая идея. Обратите внимание на небольшой регистр ребер. Предполагается, что ‘-‘ является допустимым символом. этот объект {model:'---', type:'-'} и this object {model:'--',type:'--'} будут сгруппированы, хотя они не имеют одинаковой модели и типа

4. Но @yonBav я не согласен с вашим примером, значение разделителя должно быть выбрано в соответствии с данными, с которыми вы работаете. Фрагмент примера просто показывает, как может быть решена проблема. Контекст данных не учитывался.

5. @IOR88 Используйте сокращение свойства groupBy(item.model SEPERATOR item.type)

Ответ №2:

Поскольку вы уже используете 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': [ ..., ... ] } }
  

Комментарии:

1. Спасибо, что прошли через это, имеет смысл! Я слишком много думал об этом.

2. Вы можете использовать property сокращение и просто сделать _.groupBy(selectedVehicles, 'makeCode')

Ответ №3:

Следующий код работает, если вы сосредоточены на результате:

в моем случае Brand и Item Code являются свойствами

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

Комментарии:

1. хороший ответ. если бы только вам потребовалось на 10 секунд больше, чтобы действительно заметить, что формат вывода, который он задал, отличается 🙂

2. Не все, кто попадает сюда, сталкиваются с точно такой же проблемой, как указано в вопросе. Этот ответ предназначен для таких случаев. @Dheeraj

Ответ №4:

Вы можете использовать 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);  

Комментарии:

1. Что делает этот оператор? var makeCode = map[obj.makeCode] = map[obj.makeCode] || {};

2. Основная идея моего младшего в этом var makeCode = map[obj.makeCode] = map[obj.makeCode] || {}; заключается в том, чтобы map[obj.makeCode] назначить {} или и пустой объект map[obj.makeCode] map[obj.makeCode] (он используется в качестве запасного варианта на случай, если map[obj.makeCode] еще не существует), и назначить makeCode для,,. Я должен признать, что это не самый читаемый код в мире. Я расскажу об этом самому себе, когда встречу его 🙂

Ответ №5:

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

Комментарии:

1. Я отредактировал ваш фрагмент кода, но не уверен, что это делает его лучше. Вероятно, это не должны быть выполняемые фрагменты кода, если это не полный пример кода.

Ответ №6:

 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>  

Ответ №7:

Для тех, кто ищет что-то похожее на владельца, но не имеет логического значения в качестве фильтра в качестве первого свойства, вы можете создать двойную группуby:

 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}}
}
  

Комментарии:

1. Ок, отлично ! 🙂

Ответ №8:

Группировка и фильтрация объекта или массива без Lodash на чистом JS:

 // Группировка и фильтрация объекта или массива без 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);  

Скопировано из VK

Ответ №9:

Вам не нужно повторять весь объект массива. с помощью loadash вы можете сделать просто, как показано ниже:

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