2016-02-19 6 views
1

Я пытаюсь определить лучший тип данных для сортировки массива объектов в группы, определенные свойствами. У меня есть массив объектов, например:Группировка по нескольким полям на объект

var people = [ 
    { 
     name: 'Pete', 
     gender: 'Male', 
     age: 22 

    }, 
    { 
     name: 'Samantha', 
     gender: 'Female', 
     age: 20 

    }, 
    { 
     name: 'Frank', 
     gender: 'Male', 
     age: 22 

    }, 
    { 
     name: 'Gary', 
     gender: 'Male', 
     age: 21 

    }, 
    { 
     name: 'Maria', 
     gender: 'Female', 
     age: 20 

    }, 
    { 
     name: 'Hannah', 
     gender: 'Female', 
     age: 21 

    }, 
    { 
     name: 'Pete', 
     gender: 'Male', 
     age: 20 

    } 
]; 

Мне нужно сгруппировать эти объекты в произвольно определенную группу. Например:

  1. Группа 1: gender
  2. Группа 2: age

(Это может быть определено сервером и может измениться, чтобы содержать третью группу, если мы хотим.)

Что затем дает мне (визуально):

Male: 
    21: 
    Gary 
    22: 
    Pete 
    Frank 

Female 
    20: 
    Samantha 
    Maria 
    21: 
    Hannah 

I думаю, что соответствующий тип данных будет объектом объектов. Тая .:

{ 
    Male: { 
     21: { 
      [ 
       { 
        name: 'Gary', 
        gender: 'Male', 
        age: 21 

       } 
      ] 
     }, 
     22: { 
      [ 
       { 
        name: 'Pete', 
        gender: 'Male', 
        age: 22 

       }, 
       { 
        name: 'Frank', 
        gender: 'Male', 
        age: 22 

       } 
      ] 
     } 
    }, 
    Female: { 
     20: { 
      [ 
       { 
        name: 'Samantha', 
        gender: 'Female', 
        age: 20 

       }, 
       { 
        name: 'Maria', 
        gender: 'Female', 
        age: 20 

       } 
      ] 
     }, 
     21: { 
      [ 
       { 
        name: 'Hannah', 
        gender: 'Female', 
        age: 21 

       } 
      ] 
     } 
    } 
} 

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

Там полезная утилита в underscore.js называется _.groupBy(arr, callback), которые можно использовать следующим образом:

_.groupBy(people, function (person) { 
    var props = ['gender', 'age'], // server-defined 
     prop = []; 

    for (var i = 0, length = props.length; i < length; i++) { 
     prop.push(person[props[i]]); 
    } 

    return prop.join('|'); 
}); 

Это дает мне объект 1-глубины, которые я могу использовать for...in цикл для перебора ключей, и петля чтобы построить объект выше, но это тот бит кода, на который я застрял.

Возвращаемый объект будет:

{ 
    "Male|22": [ 
     { 
      "name": "Pete", 
      "gender": "Male", 
      "age": 22 
     }, 
     { 
      "name": "Frank", 
      "gender": "Male", 
      "age": 22 
     } 
    ], 
    "Female|20": [ 
     { 
      "name": "Samantha", 
      "gender": "Female", 
      "age": 20 
     }, 
     { 
      "name": "Maria", 
      "gender": "Female", 
      "age": 20 
     } 
    ], 
    "Male|21": [ 
     { 
      "name": "Gary", 
      "gender": "Male", 
      "age": 21 
     } 
    ], 
    "Female|21": [ 
     { 
      "name": "Hannah", 
      "gender": "Female", 
      "age": 21 
     } 
    ], 
    "Male|20": [ 
     { 
      "name": "Pete", 
      "gender": "Male", 
      "age": 20 
     } 
    ] 
} 

Я имею в виду то пробегаем по каждому ключу в объекте, расщепление на трубе (|) и с помощью рекурсии построить новый объект объектов, содержащих групп/массива данных.

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

Есть ли лучший способ, которым я не хватает?

ответ

2

Может быть, это поможет вам. Он использует массив со свойствами объекта, и результат сгруппирован по содержимому свойств.

Цикл forEach выполняет итерацию по данным. Цикл reduce предназначен для генерирования сгруппированных свойств для каждой данной группы, и если он является последним, он возвращает массив, если он еще не существует.

Последний шаг - вывести значение одного из людей в массив.

var people = [{ name: 'Pete', gender: 'Male', age: 22 }, { name: 'Samantha', gender: 'Female', age: 20 }, { name: 'Frank', gender: 'Male', age: 22 }, { name: 'Gary', gender: 'Male', age: 21 }, { name: 'Maria', gender: 'Female', age: 20 }, { name: 'Hannah', gender: 'Female', age: 21 }, { name: 'Pete', gender: 'Male', age: 20 }], 
 
    groups = ['gender', 'age'], 
 
    grouped = {}; 
 

 
people.forEach(function (a) { 
 
    groups.reduce(function (o, g, i) {       // take existing object, 
 
     o[a[g]] = o[a[g]] || (i + 1 === groups.length ? [] : {}); // or generate new obj, or 
 
     return o[a[g]];           // at last, then an array 
 
    }, grouped).push(a); 
 
}); 
 

 
document.write('<pre>' + JSON.stringify(grouped, 0, 4) + '</pre>');

+0

Это работает! И группировка допускает произвольную длину. :) Можете ли вы описать это немного подробнее? – keldar

+0

У меня было что-то подобное - но это было ужасно многословно и хотелось чего-то гораздо более кратким :) - это потрясающе! – keldar

1

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

var obj = people.reduce(function (p, c, i) { 
    p[c.gender].push(c); 
    return p; 
}, { Male: [], Female: [] }); 

Если вы хотите, чтобы затем фильтровать эти массивы можно создавать функции, как это:

function getMaleByAge(obj, age) { 
    return obj.Male.filter(function (el) { 
    return el.age === age; 
    }) 
} 

getMaleByAge(obj, 21); // { name: "Gary", gender: "Male", age: 21 } 

DEMO

+0

Благодаря @andy - Единственная проблема, объект * может * содержать дополнительные свойства (я использовал этот пример, чтобы упростить сценарий) и группы может быть произвольный уровень. Таким образом, каждый объект 'person' может содержать, например, 20 свойств, и мы можем организовать их в 5 групп. Имеют смысл? – keldar

+1

Затем просто отфильтруйте исходные данные по своему усмотрению. Просто женщины? 'people.filter (function (el) {return el.gender === 'Female';});' Вместо того, чтобы переупорядочить весь набор данных, просто выберите группы данных, с которыми вам нужно работать. – Andy

+1

Даже женщины в возрасте 21 года: 'people.filter (function (el) {return el.gender === 'Female' && el.age === 21;});'. Просто. Я думаю, вы делаете гораздо больше работы, чтобы вы делали это так, как вы планировали. Надеюсь, что это сработает независимо :) – Andy

1

Использование _.groupBy & _.map

var output _.map(_.groupBy(people,'gender'),function(obj,key){ 
    var temp = {}; 
temp[key] = _.groupBy(obj,'age') 
    return temp; 
}); 

console.log(JSON.stringify(output,null,' ')) 

Даст следующий вывод

[{ 
    "Male": { 
    "20": [ 
    { 
    "name": "Pete", 
    "gender": "Male", 
    "age": 20 
    } 
    ], 
    "21": [ 
    { 
    "name": "Gary", 
    "gender": "Male", 
    "age": 21 
    } 
    ], 
    "22": [ 
    { 
    "name": "Pete", 
    "gender": "Male", 
    "age": 22 
    }, 
    { 
    "name": "Frank", 
    "gender": "Male", 
    "age": 22 
    } 
    ] 
    } 
}, 
{ 
    "Female": { 
    "20": [ 
    { 
    "name": "Samantha", 
    "gender": "Female", 
    "age": 20 
    }, 
    { 
    "name": "Maria", 
    "gender": "Female", 
    "age": 20 
    } 
    ], 
    "21": [ 
    { 
    "name": "Hannah", 
    "gender": "Female", 
    "age": 21 
    } 
    ] 
    } 
} 
] 
+0

Это выглядит очень интересно - я собираюсь реализовать это сейчас (дайте мне пять), так как у меня много тестовых данных, чтобы попробовать. – keldar

+0

Можно ли это использовать для произвольной длины свойств группировки (т. Е. Если у меня есть 3 свойства, а не 2)? – keldar

+0

Думая об этом, я могу использовать рекурсию ... имея игру сейчас. – keldar

0

Я действительно не понимаю, почему люди всегда используют рамки для такого рода вещей. Ваниль faster и это не слишком трудно кода ...

var people = [ { name: 'Pete', gender: 'Male', age: 22 },  { name: 'Samantha', gender: 'Female', age: 20 },  { name: 'Frank', gender: 'Male', age: 22 },  { name: 'Gary', gender: 'Male', age: 21 },  { name: 'Maria', gender: 'Female', age: 20 },  { name: 'Hannah', gender: 'Female', age: 21 }, { name: 'Pete', gender: 'Male', age: 20 }]; 
 

 

 
var grouped = {}; // final object 
 
for (var i=0,len=people.length,p;i<len;i++) { // faster than .forEach 
 
    p = people[i]; 
 

 
    if (grouped[p.gender] === undefined) // twice faster then hasOwnProperty 
 
    grouped[p.gender] = {}; 
 

 
    if (grouped[p.gender][p.age] === undefined) 
 
    grouped[p.gender][p.age] = []; 
 

 
    grouped[p.gender][p.age].push(p); // your groupby is HERE xD 
 
} 
 
document.getElementById('grouped').innerHTML = JSON.stringify(grouped, null, 2)
<pre id="grouped"></pre>

Смежные вопросы