JavaScript: 如何对嵌套数组进行分组
JavaScript: How to group nested array
我正在尝试在 React Native 中使用 SectionList 显示数据。我已经编写了下面的代码来显示我想要完成的事情。
我希望数据首先按 date
分组在一起,然后在该日期内,我需要按位置分组。常规 JavaScript 解决方案将起作用。它有一个 title
和 data
键很重要。
我的输入数据是这种格式:
[ { game_id: 1171,
date: '2018-11-17',
location: 'Plaza'
},
{ game_id: 1189,
date: '2018-11-17',
location: 'Field - Kickball'
},
{ game_id: 1489,
date: '2018-11-16',
location: 'Field - Kickball'
},
{ game_id: 1488,
date: '2018-11-16',
location: 'Field - Soccer'
}
]
我需要从上面的数据数组中获取以下输出:
data = [{
title: "2018-11-17",
data: [{
title: "Field - Kickball",
data: [{
game_id: 1189,
date: '2018-11-17',
location: 'Field - Kickball'
}]
},
{
title: "Plaza",
data: [{
game_id: 1171,
date: '2018-11-17',
location: 'Plaza'
}]
}
]
},
{
title: "2018-11-16",
data: [{
title: "Field - Kickball",
data: [{
game_id: 1489,
date: '2018-11-16',
location: 'Field - Kickball'
}]
},
{
title: "Field - Soccer",
data: [{
game_id: 1488,
date: '2018-11-16',
location: 'Field - Soccer'
}]
}
]
}
]
我已经试过了:
const games = [data here]
var groups = _(games)
.groupBy(x => x.date)
.map(value => {
return _.groupBy(value, 'location')
.map(({key, value}) => ({title: key, data: value}))
})
.map((value, key) => {
return ({title: value[Object.keys(value)[0]][0].date, data: value})
})
你可以在没有 lodash 的情况下使用 ES6 做这样的事情:
let arr = [ { game_id: 1171, date: '2018-11-17', location: 'Plaza' }, { game_id: 1189, date: '2018-11-17', location: 'Field - Kickball' }, { game_id: 1489, date: '2018-11-16', location: 'Field - Kickball' }, { game_id: 1488, date: '2018-11-16', location: 'Field - Soccer' } ]
let groupByfield = (data, field) => data.reduce((r, c) => {
let key = c[field]
r[key] = r[key] || {title: key, data: []}
r[key].data = [...(r[key].data || []), c]
return r
}, {})
let result = Object.values(groupByfield(arr, 'date'))
.map(x => ({
title: x.title,
data: Object.values(groupByfield(x.data, 'location'))
})
)
console.log(result)
我们的想法是创建您的自定义 groupBy
函数,然后将其用于您的分组。
我们正在使用 Array.reduce, Array.map and Object.values
如果你想要一个标准的解决方案,那么你可以先减少到一个对象,然后 return 该对象的值,然后在输出上再次分组 :)
function groupBy( arr, prop ) {
return Object.values( arr.reduce( ( aggregate, item ) => {
const val = item[prop];
if (!aggregate[val]) {
aggregate[val] = {
[prop]: val,
data: []
};
}
aggregate[val].data.push( item );
return aggregate;
}, {} ) );
}
const games = [ { game_id: 1171,
date: '2018-11-17',
location: 'Plaza'
},
{ game_id: 1189,
date: '2018-11-17',
location: 'Field - Kickball'
},
{ game_id: 1489,
date: '2018-11-16',
location: 'Field - Kickball'
},
{ game_id: 1488,
date: '2018-11-16',
location: 'Field - Soccer'
}
];
const grouped = groupBy( games, 'date' )
.map( item => ({ ...item, data: groupBy( item.data, 'location' ) }) );
console.log( grouped );
请注意,我只是使用提取的道具作为目标 属性 进行分组,如果您想要 title
,只需将 [prop]: val
更改为 'title': val
然后你可以让你的第二个分组更容易一点:)
有几种方法可以实现这一点,但是一种不需要第三方依赖项(如 Underscore 或 Lodash)的简单方法可以使用 built-in Array#reduce() 方法实现,如下所示。
有关此解决方案如何工作的详细信息,请参阅以下代码片段中的文档:
const input = [ { game_id: 1171, date: '2018-11-17', location: 'Plaza' }, { game_id: 1189, date: '2018-11-17', location: 'Field - Kickball' }, { game_id: 1489, date: '2018-11-16', location: 'Field - Kickball' }, { game_id: 1488, date: '2018-11-16', location: 'Field - Soccer' } ];
/* Reduce input data to required nested sub-array structure */
const data = input.reduce((result, item) => {
/* Construct item to be inserted into sub array for this item.date
in resulting data object */
const resultItem = {
title: item.location,
data: [{
game_id: item.game_id,
date: item.date,
location: item.location
}]
};
/* Find existing item in result array with title that matches date */
const resultDateList = result.find(i => i.title === item.date);
if (resultDateList) {
/* If matching sublist found, add constructed item to it's data array */
resultDateList.data.push(resultItem);
} else {
/* If not matching sublist found, add a new one to the result for this item
and pre-populate the data array with new item*/
result.push({
title: item.date,
data: [resultItem]
});
}
return result;
}, [])
console.log(data)
希望对您有所帮助!
使用_.flow()
生成一个函数,可以按字段对数组进行分组,并将其转换为{标题,数据}的格式。该函数还应该接受数据转换器。现在你可以递归使用它来多次分组了。
const { identity, flow, partialRight: pr, groupBy, map } = _
const groupByKey = (key, transformer = identity) => flow(
pr(groupBy, key),
pr(map, (data, title) => ({
title,
data: transformer(data)
}))
)
const data = [{"game_id":1171,"date":"2018-11-17","location":"Plaza"},{"game_id":1189,"date":"2018-11-17","location":"Field - Kickball"},{"game_id":1489,"date":"2018-11-16","location":"Field - Kickball"},{"game_id":1488,"date":"2018-11-16","location":"Field - Soccer"}]
const result = groupByKey('date', groupByKey('location'))(data)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
与lodash/fp相同的想法:
const { identity, flow, groupBy, map, get } = _
const groupByKey = (key, transformer = identity) => flow(
groupBy(key),
map(data => ({
title: get([0, key], data),
data: transformer(data)
}))
)
const data = [{"game_id":1171,"date":"2018-11-17","location":"Plaza"},{"game_id":1189,"date":"2018-11-17","location":"Field - Kickball"},{"game_id":1489,"date":"2018-11-16","location":"Field - Kickball"},{"game_id":1488,"date":"2018-11-16","location":"Field - Soccer"}]
const result = groupByKey('date', groupByKey('location'))(data)
console.log(result)
<script src='https://cdn.jsdelivr.net/g/lodash@4(lodash.min.js+lodash.fp.min.js)'></script>
我正在尝试在 React Native 中使用 SectionList 显示数据。我已经编写了下面的代码来显示我想要完成的事情。
我希望数据首先按 date
分组在一起,然后在该日期内,我需要按位置分组。常规 JavaScript 解决方案将起作用。它有一个 title
和 data
键很重要。
我的输入数据是这种格式:
[ { game_id: 1171,
date: '2018-11-17',
location: 'Plaza'
},
{ game_id: 1189,
date: '2018-11-17',
location: 'Field - Kickball'
},
{ game_id: 1489,
date: '2018-11-16',
location: 'Field - Kickball'
},
{ game_id: 1488,
date: '2018-11-16',
location: 'Field - Soccer'
}
]
我需要从上面的数据数组中获取以下输出:
data = [{
title: "2018-11-17",
data: [{
title: "Field - Kickball",
data: [{
game_id: 1189,
date: '2018-11-17',
location: 'Field - Kickball'
}]
},
{
title: "Plaza",
data: [{
game_id: 1171,
date: '2018-11-17',
location: 'Plaza'
}]
}
]
},
{
title: "2018-11-16",
data: [{
title: "Field - Kickball",
data: [{
game_id: 1489,
date: '2018-11-16',
location: 'Field - Kickball'
}]
},
{
title: "Field - Soccer",
data: [{
game_id: 1488,
date: '2018-11-16',
location: 'Field - Soccer'
}]
}
]
}
]
我已经试过了:
const games = [data here]
var groups = _(games)
.groupBy(x => x.date)
.map(value => {
return _.groupBy(value, 'location')
.map(({key, value}) => ({title: key, data: value}))
})
.map((value, key) => {
return ({title: value[Object.keys(value)[0]][0].date, data: value})
})
你可以在没有 lodash 的情况下使用 ES6 做这样的事情:
let arr = [ { game_id: 1171, date: '2018-11-17', location: 'Plaza' }, { game_id: 1189, date: '2018-11-17', location: 'Field - Kickball' }, { game_id: 1489, date: '2018-11-16', location: 'Field - Kickball' }, { game_id: 1488, date: '2018-11-16', location: 'Field - Soccer' } ]
let groupByfield = (data, field) => data.reduce((r, c) => {
let key = c[field]
r[key] = r[key] || {title: key, data: []}
r[key].data = [...(r[key].data || []), c]
return r
}, {})
let result = Object.values(groupByfield(arr, 'date'))
.map(x => ({
title: x.title,
data: Object.values(groupByfield(x.data, 'location'))
})
)
console.log(result)
我们的想法是创建您的自定义 groupBy
函数,然后将其用于您的分组。
我们正在使用 Array.reduce, Array.map and Object.values
如果你想要一个标准的解决方案,那么你可以先减少到一个对象,然后 return 该对象的值,然后在输出上再次分组 :)
function groupBy( arr, prop ) {
return Object.values( arr.reduce( ( aggregate, item ) => {
const val = item[prop];
if (!aggregate[val]) {
aggregate[val] = {
[prop]: val,
data: []
};
}
aggregate[val].data.push( item );
return aggregate;
}, {} ) );
}
const games = [ { game_id: 1171,
date: '2018-11-17',
location: 'Plaza'
},
{ game_id: 1189,
date: '2018-11-17',
location: 'Field - Kickball'
},
{ game_id: 1489,
date: '2018-11-16',
location: 'Field - Kickball'
},
{ game_id: 1488,
date: '2018-11-16',
location: 'Field - Soccer'
}
];
const grouped = groupBy( games, 'date' )
.map( item => ({ ...item, data: groupBy( item.data, 'location' ) }) );
console.log( grouped );
请注意,我只是使用提取的道具作为目标 属性 进行分组,如果您想要 title
,只需将 [prop]: val
更改为 'title': val
然后你可以让你的第二个分组更容易一点:)
有几种方法可以实现这一点,但是一种不需要第三方依赖项(如 Underscore 或 Lodash)的简单方法可以使用 built-in Array#reduce() 方法实现,如下所示。
有关此解决方案如何工作的详细信息,请参阅以下代码片段中的文档:
const input = [ { game_id: 1171, date: '2018-11-17', location: 'Plaza' }, { game_id: 1189, date: '2018-11-17', location: 'Field - Kickball' }, { game_id: 1489, date: '2018-11-16', location: 'Field - Kickball' }, { game_id: 1488, date: '2018-11-16', location: 'Field - Soccer' } ];
/* Reduce input data to required nested sub-array structure */
const data = input.reduce((result, item) => {
/* Construct item to be inserted into sub array for this item.date
in resulting data object */
const resultItem = {
title: item.location,
data: [{
game_id: item.game_id,
date: item.date,
location: item.location
}]
};
/* Find existing item in result array with title that matches date */
const resultDateList = result.find(i => i.title === item.date);
if (resultDateList) {
/* If matching sublist found, add constructed item to it's data array */
resultDateList.data.push(resultItem);
} else {
/* If not matching sublist found, add a new one to the result for this item
and pre-populate the data array with new item*/
result.push({
title: item.date,
data: [resultItem]
});
}
return result;
}, [])
console.log(data)
希望对您有所帮助!
使用_.flow()
生成一个函数,可以按字段对数组进行分组,并将其转换为{标题,数据}的格式。该函数还应该接受数据转换器。现在你可以递归使用它来多次分组了。
const { identity, flow, partialRight: pr, groupBy, map } = _
const groupByKey = (key, transformer = identity) => flow(
pr(groupBy, key),
pr(map, (data, title) => ({
title,
data: transformer(data)
}))
)
const data = [{"game_id":1171,"date":"2018-11-17","location":"Plaza"},{"game_id":1189,"date":"2018-11-17","location":"Field - Kickball"},{"game_id":1489,"date":"2018-11-16","location":"Field - Kickball"},{"game_id":1488,"date":"2018-11-16","location":"Field - Soccer"}]
const result = groupByKey('date', groupByKey('location'))(data)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
与lodash/fp相同的想法:
const { identity, flow, groupBy, map, get } = _
const groupByKey = (key, transformer = identity) => flow(
groupBy(key),
map(data => ({
title: get([0, key], data),
data: transformer(data)
}))
)
const data = [{"game_id":1171,"date":"2018-11-17","location":"Plaza"},{"game_id":1189,"date":"2018-11-17","location":"Field - Kickball"},{"game_id":1489,"date":"2018-11-16","location":"Field - Kickball"},{"game_id":1488,"date":"2018-11-16","location":"Field - Soccer"}]
const result = groupByKey('date', groupByKey('location'))(data)
console.log(result)
<script src='https://cdn.jsdelivr.net/g/lodash@4(lodash.min.js+lodash.fp.min.js)'></script>