2015-07-28 5 views
24

Мой массив что-то вроде этого:Группировка элементов массива с использованием объекта

myArray = [ 
    {group: "one", color: "red"} 
    {group: "two", color: "blue"} 
    {group: "one", color: "green"} 
    {group: "one", color: "black"} 
] 

Я хочу, чтобы преобразовать это в:

myArray = [ 
    {group: "one", color: ["red", "green", "black"]} 
    {group: "two", color: ["blue"]} 
] 

Таким образом, в основном, группа по group.

Пытаюсь:

for (i in myArray){ 
    var group = myArray[i].group; 
    //myArray.push(group, {???}) 
} 

Я просто не знаю, как обращаться с группирования аналогичных значений группы.

+0

Вы пробовали что-нибудь еще? На SO уже много тесно связанных вопросов. См. [This] (http://stackoverflow.com/questions/30893667/group-by-json-array-using-jquery), [это] (http://stackoverflow.com/questions/12873228/javascript-group- by-array) и [this] (http://stackoverflow.com/questions/25676026/group-array-with-sub-array). –

+0

Там много синтаксических ошибок. Пожалуйста, проверьте свой код перед публикацией. – 1983

ответ

21

Во-первых, в JavaScript, как правило, не рекомендуется перебирать массивы с использованием for ... in. См. Why is using "for...in" with array iteration a bad idea?.

Таким образом, вы можете попробовать что-то вроде этого:

var groups = {}; 
for (var i = 0; i < myArray.length; i++) { 
    var groupName = myArray[i].group; 
    if (!groups[groupName]) { 
    groups[groupName] = []; 
    } 
    groups[groupName].push(myArray[i].color); 
} 
myArray = []; 
for (var groupName in groups) { 
    myArray.push({group: groupName, color: groups[groupName]}); 
} 

Используя промежуточный groups объект здесь помогает ускорить процесс, потому что это позволяет избежать вложенности циклов поиска с помощью массивов. Кроме того, потому что groups является объектом (а не массивом), итерацией по нему с использованием for ... in.

Добавление

FWIW, если вы хотите, чтобы избежать дублирования записей цвета в результате массивов можно добавить if заявление выше линии groups[groupName].push(myArray[i].color); для защиты от дублей. Используя jQuery, это будет выглядеть так:

if (!$.inArray(myArray[i].color, groups[groupName])) { 
    groups[groupName].push(myArray[i].color); 
} 

Без JQuery вы можете добавить функцию, которая делает то же самое, как inArray JQuery в:

Array.prototype.contains = function(value) { 
    for (var i = 0; i < this.length; i++) { 
    if (this[i] === value) 
     return true; 
    } 
    return false; 
} 

, а затем использовать его как это:

if (!groups[groupName].contains(myArray[i].color)) { 
    groups[groupName].push(myArray[i].color); 
} 

Обратите внимание, что в любом в случае, если вы собираетесь немного замедлить работу из-за всей дополнительной итерации, поэтому, если вам не нужно избегать дублирования цветовых записей в массивах результатов, я бы рекомендовал избегать этого дополнительного сотрудничества де. Там

+1

Прошу прощения, но это не работает, если у вас есть два одинаковых объекта в '_myArray_. С var myArray = [ {group: "one", color: "red"}, {group: "two", color: "blue"}, {group: "one", color: "green"} , {group: "one", color: "black"}, {group: "one", color: "black"} ]; ' Вы получите' myArray [0] .color = ["red «зеленый», «черный», «черный»] « – Lends

+0

« Не работает »является субъективным. OP никогда не заявлял, что делать в этом случае. – Jan

+0

@ Прилагает, чтобы ОП не указывал это как требование. На самом деле, учитывая комментарий от ОП, я бы сказал, что это решение действительно «работает». – neuronaut

4

Один из вариантов:

var res = myArray.reduce(function(groups, currentValue) { 
    if (groups.indexOf(currentValue.group) === -1) { 
     groups.push(currentValue.group); 
    } 
    return groups; 
}, []).map(function(group) { 
    return { 
     group: group, 
     color: myArray.filter(function(_el) { 
      return _el.group === group; 
     }).map(function(_el) { return _el.color; }) 
    } 
}); 

http://jsfiddle.net/dvgwodxq/

+3

Очень элегантный и самодостаточный, приятный. Моя единственная проблема заключается в том, что она не так легко читается, как более простой цикл 'for'. – Jan

+1

уменьшить, indexOf, карта, фильтр, карта кажется слишком сложной для меня. – 1983

+0

@KingMob Определить «комплекс». Это обычные методы массива. – undefined

2

Эта версия имеет преимущество, что опротестовать ключи уникальны. Мы обрабатываем исходный массив и собираем цвета по группе в новом объекте. Затем создайте новые объекты из этой группы -> массив массивов цветов.

var myArray = [{ 
     group: "one", 
     color: "red" 
    }, { 
     group: "two", 
     color: "blue" 
    }, { 
     group: "one", 
     color: "green" 
    }, { 
     group: "one", 
     color: "black" 
    }]; 

    //new object with keys as group and 
    //color array as value 
    var newArray = {}; 

    //iterate through each element of array 
    myArray.forEach(function(val) { 
     var curr = newArray[val.group] 

     //if array key doesnt exist, init with empty array 
     if (!curr) { 
     newArray[val.group] = []; 
     } 

     //append color to this key 
     newArray[val.group].push(val.color); 
    }); 

    //remove elements from previous array 
    myArray.length = 0; 

    //replace elements with new objects made of 
    //key value pairs from our created object 
    for (var key in newArray) { 
     myArray.push({ 
     'group': key, 
     'color': newArray[key] 
     }); 
    } 

Пожалуйста, обратите внимание, что это не учитывает повторяющиеся цвета той же группы, так что можно иметь несколько одного и того же цвета в массиве для одной группы.

0

Вы можете сделать что-то вроде этого:

function convert(items) { 
    var result = []; 

    items.forEach(function (element) { 
     var existingElement = result.filter(function (item) { 
      return item.group === element.group; 
     })[0]; 

     if (existingElement) { 
      existingElement.color.push(element.color); 
     } else { 
      element.color = [element.color]; 
      result.push(element); 
     } 

    }); 

    return result; 
} 
35

Start путем создания отображения имен групп до значений. Затем преобразуйте в желаемый формат.

var myArray = [ 
 
    {group: "one", color: "red"}, 
 
    {group: "two", color: "blue"}, 
 
    {group: "one", color: "green"}, 
 
    {group: "one", color: "black"} 
 
]; 
 

 
var group_to_values = myArray.reduce(function (obj, item) { 
 
    obj[item.group] = obj[item.group] || []; 
 
    obj[item.group].push(item.color); 
 
    return obj; 
 
}, {}); 
 

 
var groups = Object.keys(group_to_values).map(function (key) { 
 
    return {group: key, color: group_to_values[key]}; 
 
}); 
 

 
var pre = document.createElement("pre"); 
 
pre.innerHTML = "groups:\n\n" + JSON.stringify(groups, null, 4); 
 
document.body.appendChild(pre);

Использование методов экземпляра массива, такие как reduce и map дает мощные конструкции более высокого уровня, которые могут сохранить вам много боли зацикливание вручную.

+2

Работает как очарование, спасибо. –

+0

Отличный ответ. С Object.entries и ES6 мы также можем сделать 'groups = Object.entries (group_to_values) .map (([group, color]) => ({group, color}));' – a15n

+0

Awesome! моя проблема заключалась в том, чтобы вытащить ключи для создания массива объектов с помощью 'Object.keys' – William

4

groupby метод пользы lodash в

Создает объект, состоящий из ключей, генерируемых из результатов работы каждого элемента коллекции через iteratee. Порядок группируемых значений определяется порядком их возникновения в коллекции. Соответствующее значение каждой клавиши представляет собой массив элементов, ответственных за генерацию ключа. Итератор вызывается одним аргументом: (значение).

Таким образом, с помощью lodash вы можете получить то, что хотите, в одной строке. Ниже

let myArray = [ 
 
    {group: "one", color: "red"}, 
 
    {group: "two", color: "blue"}, 
 
    {group: "one", color: "green"}, 
 
    {group: "one", color: "black"}, 
 
] 
 
let grouppedArray=_.groupBy(myArray,'group') 
 
console.log(grouppedArray)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>

1

Помимо указанных подходов с двупроходном подходом, вы могли бы принять подход одного цикла, нажав на группу, если новая группа найдена.

var array = [{ group: "one", color: "red" }, { group: "two", color: "blue" }, { group: "one", color: "green" }, { group: "one", color: "black" }], 
 
    groups = Object.create(null), 
 
    grouped = []; 
 

 
array.forEach(function (o) { 
 
    if (!groups[o.group]) { 
 
     groups[o.group] = []; 
 
     grouped.push({ group: o.group, color: groups[o.group] }); 
 
    } 
 
    groups[o.group].push(o.color); 
 
}); 
 

 
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }

-1
var array = [{ 
     id: "123", 
     name: "aaaaaaaa" 
    }, { 
     id: "123", 
     name: "aaaaaaaa" 
    }, { 
     id: '456', 
     name: 'bbbbbbbbbb' 
    }, { 
     id: '789', 
     name: 'ccccccccc' 
    }, { 
     id: '789', 
     name: 'ccccccccc' 
    }, { 
     id: '098', 
     name: 'dddddddddddd' 
    }]; 
//if you want to group this array 
group(array, key) { 
    console.log(array); 
    let finalArray = []; 
    array.forEach(function(element) { 
    var newArray = []; 
    array.forEach(function(element1) { 
     if (element[key] == element1[key]) { 
      newArray.push(element) 
     } 
    }); 
    if (!(finalArray.some(arrVal => newArray[0][key] == arrVal[0][key]))) { 
     finalArray.push(newArray); 
    } 
    }); 
    return finalArray 
} 
//and call this function 
groupArray(arr, key) { 
    console.log(this.group(arr, key)) 
} 
Смежные вопросы