我想做一个雷达图,但我无法控制api输出,所以我必须将一个复杂的数据转换成某种形式。我被困了至少半个小时。
如何转换此原始数据
const raw = [{
"device_info": {
"device_id": 123,
"name": "iphone",
},
"age_data": [{
"age_range": "0-10",
"total_count": 15,
"man": 6,
"women": 9
}, {
"age_range": "11-20",
"total_count": 11,
"man": 7,
"women": 4
}]
}, {
"device_info": {
"device_id": 456,
"name": "android",
},
"age_data": [{
"age_range": "0-10",
"total_count": 1,
"man": 1,
"women": 0
}, {
"age_range": "11-20",
"total_count": 2,
"man": 0,
"women": 2
}]
}]
进入这个
const data = [{
age_group: '0-20',
iphone: 26,
android: 3
}, {
age_group: '21-30',
iphone: 0,
android: 0
}, ];
https://jsfiddle.net/cqmyganr/2
这就是我的尝试:
const age_group = raw[0].age_data.map(obj => ({
age_group: obj.age_range
}))
const cams = raw.map(obj => ({
device_id: obj.device_info.device_id,
device_name: obj.device_info.name
}))
console.log(cams)
const age_group_with_cams = age_group.map(obj =>
Object.assign({}, obj, ...cams)
)
console.log(age_group_with_cams)
答案 0 :(得分:2)
好的,我会使用reduce
,因为您正在将数据转换为另一种形式list-of-devices-with-age-info
=> list-of-ages-with-devices
列表可以有不同的长度。
这将是我的第一步,将所有设备映射到所有现有年龄
const usageByAge = raw.reduce((by_age, dev) => {
dev.age_data.forEach(age => {
if (!by_age[age.age_range]) by_age[age.age_range] = {};
by_age[age.age_range][dev.device_info.name] = age.total_count;
})
return by_age;
}, {})
// output, something like this:
usageByAge = {
"0-10": { android: 123, iphone: 321 },
"10-20": { android: 123, iphone: 321 },
}
再次从这里开始减少功能,这次创建一个规则:
isGroupInBounds = (g, lo, hi) => {
const [gl, gh] = g.split('-');
return gl >= lo && gh <= hi;
}
["0-20", "21-30"].map(age_group => {
const group = { age_group, iphone: 0, android: 0 };
const [l, h] = age_group.split('-')
Object.keys(usageByAge).forEach(inputGroup => {
if (isGroupInBounds(inputGroup, l, h)) {
Object.keys(usageByAge[inputGroup]).forEach(deviceName => {
group[deviceName] += usageByAge[inputGroup][deviceName];
})
}
})
return group;
})
Yeeah,这个非常棘手,在你的小提琴中试过它并且它有效;)谢谢你的练习!
答案 1 :(得分:1)
在阅读@ webdeb的回答后,我认为有更好的方法可以做他做的事情。这就是我想出的:
const raw = [ { device_info: { device_id: 123
, name: "iphone" }
, age_data: [ { age_range: "0-10"
, total_count: 15
, man: 6
, women: 9 }
, { age_range: "11-20"
, total_count: 11
, man: 7
, women: 4 } ] }
, { device_info: { device_id: 456
, name: "android" }
, age_data: [ { age_range: "0-10"
, total_count: 1
, man: 1
, women: 0 }
, { age_range: "11-20"
, total_count: 2
, man: 0
, women: 2 } ] } ];
// First, we convert the raw data into a relation:
const relation = [], devices = {};
const toInt = x => parseInt(x, 10);
for (const { device_info: { device_id, name }, age_data } of raw) {
devices[name] = 0; // The default total_count of every device.
for (const { age_range, total_count, man, women } of age_data) {
const [age_begin, age_end] = age_range.split("-").map(toInt);
relation.push({ device_id, name
, age_begin, age_end
, total_count, man, women });
}
}
// Next, we write a function that given an age range returns its stats:
const getAgeRange = (begin, end) =>
relation.reduce((table, tuple) => {
if (tuple.age_begin >= begin && tuple.age_end <= end) {
const row = table.find(row => row.name === tuple.name);
if (row) {
row.total_count += tuple.total_count;
row.man += tuple.man;
row.women += tuple.women;
} else table.push(Object.assign({}, tuple));
} return table;
}, []);
// Finally, we create an array of the age groups we're looking for:
const getAgeGroup = (begin, end) =>
getAgeRange(begin, end).reduce((summary, { name, total_count }) => {
summary[name] = total_count;
return summary;
}, Object.assign({ age_group: begin + "-" + end }, devices));
const data = [ getAgeGroup(0, 20)
, getAgeGroup(21, 30) ];
console.log(data);
希望这是有道理的。我试图尽可能地坚持relational model。