如何按照多个属性对JavaScript对象数组进行分组?

6

我希望将showtimesData转换为showtimesByLocationByDate

有没有什么办法可以不使用任何第三方库,只使用纯JavaScript来完成?否则,我可以使用哪个第三方库来实现?

    var showtimesData = [
        {"location":"location1", "date":"31-12-2016", "time":"1:00"},
        {"location":"location1", "date":"31-12-2016", "time":"2:00"},
        {"location":"location1", "date":"01-01-2017", "time":"3:00"},
        {"location":"location1", "date":"01-01-2017", "time":"4:00"},
        {"location":"location2", "date":"31-12-2016", "time":"1:00"},
        {"location":"location2", "date":"31-12-2016", "time":"2:00"},
        {"location":"location2", "date":"01-01-2017", "time":"3:00"},
        {"location":"location2", "date":"01-01-2017", "time":"4:00"},
    ];
    var showtimesByLocationByDate = [
        {
            "location":"location1",
            "dates":[
                {
                    "date":"31-12-2016",
                    "times":["1:00","2:00"]
                },
                {
                    "date":"01-01-2017",
                    "times":["3:00","4:00"]
                }
            ]
        },
        {
            "location":"location2",
            "dates":[
                {
                    "date":"31-12-2016",
                    "times":["1:00","2:00"]
                },
                {
                    "date":"01-01-2017",
                    "times":["3:00","4:00"]
                }
            ]
        },
    ];

它必须遵循特定的格式吗?如果日期查找是对象属性,那么速度会更快。 - MinusFour
如果我将日期设置为属性,那么我能循环遍历这些属性以获取日期字符串吗? - davidchoo12
是的,可以循环遍历对象的属性。 - MinusFour
我做了一些谷歌搜索,它是使用Object.keys函数吗? - davidchoo12
2个回答

4

本提案仅使用Array.prototype.reduce(),并使用一个临时对象引用数组项。

var showtimesData = [{ "location": "location1", "date": "31-12-2016", "time": "1:00" }, { "location": "location1", "date": "31-12-2016", "time": "2:00" }, { "location": "location1", "date": "01-01-2017", "time": "3:00" }, { "location": "location1", "date": "01-01-2017", "time": "4:00" }, { "location": "location2", "date": "31-12-2016", "time": "1:00" }, { "location": "location2", "date": "31-12-2016", "time": "2:00" }, { "location": "location2", "date": "01-01-2017", "time": "3:00" }, { "location": "location2", "date": "01-01-2017", "time": "4:00" }, ],
    showtimesByLocationByDate = showtimesData.reduce(function (r, a) {
        var o;
        if (!(a.location in r.obj)) {
            o = { location: a.location, dates: [] };
            r.obj[a.location] = { dates: o.dates };
            r.array.push(o);
        }
        if (!(a.date in r.obj[a.location])) {
            o = { date: a.date, times: [] };
            r.obj[a.location].dates.push(o);
            r.obj[a.location][a.date] = o.times;
        }
        r.obj[a.location][a.date].push(a.time);
        return r;
    }, { array: [], obj: {} }).array;

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

奖励:针对给定数据结构的通用版本

var showtimesData = [{ "location": "location1", "date": "31-12-2016", "time": "1:00" }, { "location": "location1", "date": "31-12-2016", "time": "2:00" }, { "location": "location1", "date": "01-01-2017", "time": "3:00" }, { "location": "location1", "date": "01-01-2017", "time": "4:00" }, { "location": "location2", "date": "31-12-2016", "time": "1:00" }, { "location": "location2", "date": "31-12-2016", "time": "2:00" }, { "location": "location2", "date": "01-01-2017", "time": "3:00" }, { "location": "location2", "date": "01-01-2017", "time": "4:00" }, ],
    structure = [
        { key: 'location', data: 'dates' },
        { key: 'date', data: 'times' },
        { key: 'time' }
    ],
    showtimesByLocationByDate = showtimesData.reduce(function (r, a) {
        var properties = structure.slice(),
            lastKey = properties.pop().key;

        properties.reduce(function (rr, b) {
            var o = {},
                p = {},
                key = b.key,
                value = a[key],
                array = b.data;

            if (!(value in rr.obj)) {
                o[key] = value;
                o[array] = [];
                p[array] = o[array];
                rr.obj[value] = p;
                rr.array.push(o);
            }
            return { array: rr.obj[value][array], obj: rr.obj[value] };
        }, r).array.push(a[lastKey]);

        return r;
    }, { array: [], obj: {} }).array;

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


你的回答非常精准,但我只是想知道是否有一种让它更通用的方法? - Rajesh
@Rajesh,这取决于数据结构和它需要多通用。 - Nina Scholz
我想知道是否可以创建一个函数,通过传递数据和按属性分组来返回分组值。以下是我尝试过的JSFiddle - Rajesh

4
我建议进行以下改变:

var showtimesData = [
        {"location":"location1", "date":"31-12-2016", "time":"1:00"},
        {"location":"location1", "date":"31-12-2016", "time":"2:00"},
        {"location":"location1", "date":"01-01-2017", "time":"3:00"},
        {"location":"location1", "date":"01-01-2017", "time":"4:00"},
        {"location":"location2", "date":"31-12-2016", "time":"1:00"},
        {"location":"location2", "date":"31-12-2016", "time":"2:00"},
        {"location":"location2", "date":"01-01-2017", "time":"3:00"},
        {"location":"location2", "date":"01-01-2017", "time":"4:00"},
    ];
  
var transformed = showtimesData.reduce(function(obj, show){
  //var { location, date, time } = show; //if destructuring is available
  var location = show.location,
      date = show.date,
      time = show.time,
      objLocation = obj[location] = obj[location] || { dates : { } },
      dates = objLocation.dates,
      date = dates[date] = dates[date] || [ ];

      date.push(time);
      return obj;
}, {});
results.innerHTML = JSON.stringify(transformed, null, '\t');
<pre id="results"></pre>

但如果你真的想将其转换为那样,我建议采用这个转换并将其映射到你提出的结构。

var showtimesData = [
        {"location":"location1", "date":"31-12-2016", "time":"1:00"},
        {"location":"location1", "date":"31-12-2016", "time":"2:00"},
        {"location":"location1", "date":"01-01-2017", "time":"3:00"},
        {"location":"location1", "date":"01-01-2017", "time":"4:00"},
        {"location":"location2", "date":"31-12-2016", "time":"1:00"},
        {"location":"location2", "date":"31-12-2016", "time":"2:00"},
        {"location":"location2", "date":"01-01-2017", "time":"3:00"},
        {"location":"location2", "date":"01-01-2017", "time":"4:00"},
    ];
  
var transformed = showtimesData.reduce(function(obj, show){
  //var { location, date, time } = show; //if destructuring is available
  var location = show.location,
      date = show.date,
      time = show.time,
      objLocation = obj[location] = obj[location] || { dates : { } },
      dates = objLocation.dates,
      date = dates[date] = dates[date] || [ ];

      date.push(time);
      return obj;
}, {});

var secondTransformed = Object.keys(transformed).map(function(key){
  var dates = transformed[key].dates,
      transformedDates = Object.keys(dates).map(function(key){
          return { date : key, times : dates[key] }
      });
  return { location : key, dates : transformedDates }
});
results.innerHTML = JSON.stringify(secondTransformed, null, '\t');
<pre id="results"></pre>

虽然有更好的方法来完成这个(从性能上来看)。

1
记录 console 中的 Uncaught SyntaxError: Unexpected token { - guest271314
这是解构赋值,您的浏览器还不支持。 - MinusFour
好的,会尝试在不同的浏览器中。 - guest271314
@guest271314,不用担心,我已经更改了它,现在不再使用。 - MinusFour
就此而言,在Chromium 49中,FWIW返回了预期结果。在答案中也可以包含解构版本吗? - guest271314
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接