#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);
Ответ №9:
Вам не нужно повторять весь объект массива. с помощью loadash вы можете сделать просто, как показано ниже:
_.mapValues(_.groupBy(object, 'yourKeys'))