我正在尝试使用Javascript / Node.JS将平面数据集中的所有项目组合在一起以形成分层数据集。
我有一个解决方案,但我认为这不是最优雅的,它可能会得到改善。
我在Find all objects with matching Ids javascript
中给出了答案的解决方案我的数据集如下:
let data = [{cid: 1, clbl: 'Rush Shipping', pid:5, plbl: 'FedEx'},
{cid: 2, clbl: 'Standard Shipping', pid:5, plbl: 'FedEx'},
{cid: 3, clbl: 'First Class', pid:8, plbl: 'USPS'},
{cid: 4, clbl: 'Std', pid:9, plbl: 'DHL'},
{cid: 5, clbl: 'Canada Post', pid:1, plbl: 'Canada Post'},
];
我希望我的输出是这样的:
[ { pid: 5,
plbl: 'FedEx',
methods: [
{
cid: 1,
clbl: 'Rush Shipping',
},
{
cid: 2,
clbl: 'Standard Shipping',
},
},
{ pid: 8,
plbl: 'USPS',
methods: [
{
cid: 3,
clbl: 'First Class',
},
},
{ pid: 9,
plbl: 'DHL',
methods: [
{
cid: 4,
clbl: 'Std',
},
},
{ pid: 1,
plbl: 'Canada Post',
methods: [
{
cid: 5,
clbl: 'Canada Post',
},
},
]
我整理了一些可行的代码,但我想有一种更优化的方法可以做到这一点,并认为我会将其放入SO社区。 p>
这是我的解决方法:
var roots = [];
var all = {};
data.forEach(function(item) {
all[item.pid] = item;
})
Object.keys(all).forEach(function(pid) {
var items = data.filter(x => x.pid == pid);
var addItem = {};
items.forEach(function(item, j) {
if (j === 0){
addItem = {pid:item.pid, label:item.plbl, methods:[]};
}
addItem.methods.push({cid: item.cid, label: item.clbl});
});
roots.push(addItem);
})
console.log(roots);
答案 0 :(得分:1)
从内存/速度的角度来看,我不认为这是“最佳化”的,但要短一些。
let new_data = Object.values(data.reduce(function(o, d) {
o[d.pid] = o[d.pid] || {pid: d.pid, plbl: d.plbl, methods:[]};
o[d.pid].methods.push({cid: d.cid, clbl: d.clbl});
return o;
}, {}));
基本上可以利用reduce方法来构建一个组合的all
对象。然后使用Object.values()根据存储在all
对象中的值创建一个数组,而不是手动推送它们。
答案 1 :(得分:0)
我将建议一种更困难而不是更简单的方法。但这还将涉及创建许多可重用的功能。
我是Ramda编程库的忠实拥护者(免责声明:我是它的作者之一。)因此,当我尝试执行此类操作时,我会接触Ramda。而且我发现,只需将多个简单的步骤组合在一起,就可以在其REPL中进行编码。
我与Ramda的通行证如下:
const transform = pipe(
groupBy(prop('pid')),
map(applySpec({
pid: path([0, 'pid']),
plbl: path([0, 'plbl']),
methods: project(['cid', 'clbl'])
})),
values
)
let data = [{cid: 1, clbl: 'Rush Shipping', pid:5, plbl: 'FedEx'}, {cid: 2, clbl: 'Standard Shipping', pid:5, plbl: 'FedEx'}, {cid: 3, clbl: 'First Class', pid:8, plbl: 'USPS'}, {cid: 4, clbl: 'Std', pid:9, plbl: 'DHL'}, {cid: 5, clbl: 'Canada Post', pid:1, plbl: 'Canada Post'}];
console.log(transform(data))
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://bundle.run/ramda@0.26.1"></script><script>
const {pipe, groupBy, prop, map, applySpec, path, project, values} = ramda </script>
其中的所有功能都相当可重用。因此,我可以通过包含每个函数的简单版本来启动自己的小程序库。然后这段代码同样简单,我可以在应用程序的其他地方使用这些功能。
这是使用这些功能的简化版本的另一种方法。 (请注意,我在这里将map
重命名为mapObject
,因为我编写的任何此类库都将包含一个简单的map
函数,其功能类似于Array.prototype.map
。在Ramda中,一个函数涵盖了他们两个,但这不是那么简单。)
// In my utility library
const pipe = (fn1, ...fns) => (...args) =>
fns.reduce((r, fn) => fn(r), fn1(...args))
const prop = (name) => (obj) =>
obj[name]
const values = (obj) =>
Object.values(obj)
const mapObject = (fn) => (obj) =>
Object.keys(obj).reduce((a, k) => ({...a, [k]: fn(obj[k])}), {})
const groupBy = (fn) => (xs) =>
xs.reduce((a, x) => ({...a, [fn(x)]: (a[fn(x)] || []).concat(x)}), {})
const applySpec = (s) => (o) =>
Object.entries(s).reduce((a, [k, fn]) => ({...a, [k]: fn(o)}), {})
const path = (ns) => (obj) =>
ns.reduce((v, n) => (v[n] || {}), obj)
const project = (ns) => (xs) =>
xs.map(x => ns.reduce((a, n) => ({...a, [n]: x[n]}), {}))
// In current module
const transform = pipe(
groupBy(prop('pid')),
mapObject(applySpec({
pid: path([0, 'pid']),
plbl: path([0, 'plbl']),
methods: project(['cid', 'clbl'])
})),
values
)
let data = [{cid: 1, clbl: 'Rush Shipping', pid:5, plbl: 'FedEx'}, {cid: 2, clbl: 'Standard Shipping', pid:5, plbl: 'FedEx'}, {cid: 3, clbl: 'First Class', pid:8, plbl: 'USPS'}, {cid: 4, clbl: 'Std', pid:9, plbl: 'DHL'}, {cid: 5, clbl: 'Canada Post', pid:1, plbl: 'Canada Post'}];
console.log(transform(data))
.as-console-wrapper { max-height: 100% !important; top: 0; }
<!-- Look, Ma, no Ramda -->
所有这些功能都可用in the Ramda documentation。其中许多功能更为复杂,但是这些简单的实现将使我们走很长一段路。