动态遍历深层嵌套对象并累积结果
Dynamically traversing a deep nested object and accumulating results
我正在尝试剖析一个深度嵌套的对象。遍历它并将数据拉到一个单一级别的对象,然后与一个更大的对象结合起来。我可以使用以下代码遍历它,但它不是动态的。就像,这只有在我深入了解它的 4 个对象时才有效。实际上它可以是 1 -> n 深。我还有一个函数可以检查该级别的值类型并相应地对其进行处理。现在这对我有用
run();
function run() {
const topLevelObjects = [{
field: 'pivotvalue0',
value: '111111111',
count: 36,
pivot: [{
field: 'pivotvalue1',
value: 'Y',
count: 27,
pivot: [{
field: 'pivotvalue2',
value: 'Header1',
count: 27,
pivot: [{
field: 'pivotvalue3',
value: 'Value1',
count: 14
},
{
field: 'pivotvalue3',
value: 'Value2',
count: 13
}
]
}]
},
{
field: 'pivotvalue1',
value: 'Z',
count: 9,
pivot: [{
field: 'pivotvalue2',
value: 'Header2',
count: 9,
pivot: [{
field: 'pivotvalue3',
value: 'Value1',
count: 9
}]
}]
}
]
},
{
field: 'pivotvalue0',
value: '222222222',
count: 23,
pivot: [{
field: 'pivotvalue1',
value: 'Y',
count: 23,
pivot: [{
field: 'pivotvalue2',
value: 'Header1',
count: 12,
pivot: [{
field: 'pivotvalue3',
value: 'Value2',
count: 12
}]
},
{
field: 'pivotvalue2',
value: 'Header2',
count: 11,
pivot: [{
field: 'pivotvalue3',
value: 'Value2',
count: 11
}]
}
]
}]
}
]
//pivotResponse.facet_counts.facet_pivot[pivotsString]
const accumulatedPivotInfo = []
//This is assuming you can only pivot 4 deep. Would love to figure out how to make this dynamic.
for (const level0 of topLevelObjects) {
let newPivotInfo = {}
newPivotInfo[level0.field] = level0.value
if (level0.pivot) {
for (const level1 of level0.pivot) {
newPivotInfo = {
...newPivotInfo,
...buildBasedOnType(level1, newPivotInfo)
}
if (level1.pivot) {
for (const level2 of level1.pivot) {
newPivotInfo = {
...newPivotInfo,
...buildBasedOnType(level2, newPivotInfo)
}
if (level2.pivot) {
for (const level3 of level2.pivot) {
newPivotInfo = {
...newPivotInfo,
...buildBasedOnType(level3, newPivotInfo)
}
}
}
}
}
}
}
accumulatedPivotInfo.push(newPivotInfo)
}
console.log(accumulatedPivotInfo)
}
function buildBasedOnType(level, newPivotInfo) {
if (level.field === 'pivotvalue1') {
level.value === 'Y' ? newPivotInfo['pivotvalue1.1'] = level.count : newPivotInfo['pivotvalue1.1'] = 0
level.value === 'N' ? newPivotInfo['pivotvalue1.2'] = level.count : newPivotInfo['pivotvalue1.2'] = 0
level.value === 'Z' ? newPivotInfo['pivotvalue1.3'] = level.count : newPivotInfo['pivotvalue1.3'] = 0
} else if (level.field === 'pivotvalue2' || level.field === 'pivotvalue3') {
newPivotInfo[level.field + 's'] === undefined ? newPivotInfo[level.field + 's'] = new Set([level.value]) : newPivotInfo[level.field + 's'].add(level.value)
} else {
newPivotInfo[level.field] = level.value
}
return newPivotInfo
}
这是我的最终输出结果以及我要实现的目标
pivotInfo = [
{
pivotvalue0: '111111111',
'pivotvalue1.1': 0,
'pivotvalue1.2': 0,
'pivotvalue1.3': 9,
pivotvalue2s: Set { 'Header1', 'Header2' },
pivotvalue3s: Set { 'Value1', 'Value2' }
},
{
pivotvalue0: '222222222',
'pivotvalue1.1': 23,
'pivotvalue1.2': 0,
'pivotvalue1.3': 0,
pivotvalue2s: Set { 'Header1', 'Header2' },
pivotvalue3s: Set { 'Value2' }
}
]
出于某种原因,代码运行器确实可以很好地处理集合。所以这是 jsfiddle 中设置集合的相同代码:https://jsfiddle.net/x9sa8tqm/
希望这是足够的上下文来了解我目前在做什么。现在我有数据可以通过值数组了解这些对象的深度。所以我可以检查数组长度并知道我需要走多深。使这种动态化的最佳方法是什么?所以它会弯曲到 'n' 深度而不是头部编码 4.
谢谢!
使用递归实现。
var data = [
{
field: 'pivotvalue0',
value: '200275399',
count: 36,
pivot: [{
field: 'pivotvalue0.1',
value: '200275399',
count: 36,
pivot: [{
field: 'pivotvalue0.2',
value: '200275399',
count: 36,
pivot: []
}]
}]
},
{
field: 'pivotvalue0',
value: '200746617',
count: 23,
pivot: []
}
]
var finalResult = [];
function accumulateValue(data){
data.forEach(item=>{
finalResult.push({
field: item.field,
value: item.value,
count: item.count,
});
if(item.pivot && item.pivot.length){
accumulateValue(item.pivot)
}
})
}
accumulateValue(data)
console.log(finalResult);
这是一种递归执行此操作的方法。我们 special-case 您想要专门处理的类型,对于所有其他情况,我们将 {[field]: value}
添加到我们的 运行ning 输出 object.
要注意的主要事情是递归是自动的。我们不手动编码关卡,这应该让我们可以任意深入。
// utility function
const gather = (prop) => (fn) => (o) =>
[... fn (o), ... (prop in o ? o [prop] : []) .flatMap (gather (prop) (fn))]
// helper function
const collectField = (name) => (o) => ({
[name + 's'] :
[... // comment out to get an actual set
new Set(
gather ('pivot') (
({field, value}) => field == name ? [value] : []
) (o)
)
] // comment out to get an actual set
})
// main function
const convert = (xs) =>
xs .map ((x, _, __, {field, value, count, pivot = []} = x) => Object .assign ({
...(
field == 'pivotvalue1'
? {
'pivotvalue1.1': value == 'Y' ? count: 0,
'pivotvalue1.2': value == 'N' ? count: 0,
'pivotvalue1.3': value == 'Z' ? count: 0,
}
: field == 'pivotvalue2' || field == 'pivotvalue3'
? {}
: {[field]: value}
)},
... convert (pivot),
collectField ('pivotvalue2') (x),
collectField ('pivotvalue3') (x),
))
// sample data
const topLevelObjects = [{field: "pivotvalue0", value: "111111111", count: 36, pivot: [{field: "pivotvalue1", value: "Y", count: 27, pivot: [{field: "pivotvalue2", value: "Header1", count: 27, pivot: [{field: "pivotvalue3", value: "Value1", count: 14}, {field: "pivotvalue3", value: "Value2", count: 13}]}]}, {field: "pivotvalue1", value: "Z", count: 9, pivot: [{field: "pivotvalue2", value: "Header2", count: 9, pivot: [{field: "pivotvalue3", value: "Value1", count: 9}]}]}]}, {field: "pivotvalue0", value: "222222222", count: 23, pivot: [{field: "pivotvalue1", value: "Y", count: 23, pivot: [{field: "pivotvalue2", value: "Header1", count: 12, pivot: [{field: "pivotvalue3", value: "Value2", count: 12}]}, {field: "pivotvalue2", value: "Header2", count: 11, pivot: [{field: "pivotvalue3", value: "Value2", count: 11}]}]}]}]
// demo
console .log (convert (topLevelObjects))
.as-console-wrapper {max-height: 100% !important; top: 0}
gather
是一个相当通用的辅助函数,用于递归地收集将函数应用于每个节点的结果。初始参数 prop
用于 属性 保存树的更深层的节点的名称(最常见的是 'children'
,这里是 'pivot'
。)第二个参数是一个函数,其每个节点的结果将在最后收集到一个数组中。当然,第三个参数是你的树 object 本身。这是我在这里使用的常用函数的轻微变体,因为它在将每次调用的结果包含在数组中之前将其展平。这允许提供给 return 具有单个值或没有值的数组的函数,并且输出数组仅包含实际值。 (如果我们想使用更通用的 gather
,有很多替代方法可以做这样的事情。)
我们在collectField
中使用gather
来获取所有关联的value
节点,当field
属性 匹配提供的 name
。请注意,我们不会在下面函数的主要递归调用中 运行 this 。这些结果将在稍后附上。虽然很可能通过深度递归调用来传递状态信息,但如果可以的话,我宁愿避免它。这意味着对于 pivotvalue2
和 pivotvalue3
,我们实际上进行了单独的递归。另请注意,您在此处的输出中请求 Set
,我使用数组是为了更轻松地进行演示。但我注意到您可以注释掉这两行以使其保持实际设置。
main函数将指定的字段收集到一个object中,然后在pivot
中对children进行递归调用,然后将这两个调用添加到[=19] =].
这对于性能来说并不理想,因为我们对输入进行多次递归遍历。但它是更简单的代码,如果存在可衡量的性能问题,我们可以稍后再回来。
(稍后我可能会考虑另一种方法,因为我有一个想法,但现在没有时间。)
我正在尝试剖析一个深度嵌套的对象。遍历它并将数据拉到一个单一级别的对象,然后与一个更大的对象结合起来。我可以使用以下代码遍历它,但它不是动态的。就像,这只有在我深入了解它的 4 个对象时才有效。实际上它可以是 1 -> n 深。我还有一个函数可以检查该级别的值类型并相应地对其进行处理。现在这对我有用
run();
function run() {
const topLevelObjects = [{
field: 'pivotvalue0',
value: '111111111',
count: 36,
pivot: [{
field: 'pivotvalue1',
value: 'Y',
count: 27,
pivot: [{
field: 'pivotvalue2',
value: 'Header1',
count: 27,
pivot: [{
field: 'pivotvalue3',
value: 'Value1',
count: 14
},
{
field: 'pivotvalue3',
value: 'Value2',
count: 13
}
]
}]
},
{
field: 'pivotvalue1',
value: 'Z',
count: 9,
pivot: [{
field: 'pivotvalue2',
value: 'Header2',
count: 9,
pivot: [{
field: 'pivotvalue3',
value: 'Value1',
count: 9
}]
}]
}
]
},
{
field: 'pivotvalue0',
value: '222222222',
count: 23,
pivot: [{
field: 'pivotvalue1',
value: 'Y',
count: 23,
pivot: [{
field: 'pivotvalue2',
value: 'Header1',
count: 12,
pivot: [{
field: 'pivotvalue3',
value: 'Value2',
count: 12
}]
},
{
field: 'pivotvalue2',
value: 'Header2',
count: 11,
pivot: [{
field: 'pivotvalue3',
value: 'Value2',
count: 11
}]
}
]
}]
}
]
//pivotResponse.facet_counts.facet_pivot[pivotsString]
const accumulatedPivotInfo = []
//This is assuming you can only pivot 4 deep. Would love to figure out how to make this dynamic.
for (const level0 of topLevelObjects) {
let newPivotInfo = {}
newPivotInfo[level0.field] = level0.value
if (level0.pivot) {
for (const level1 of level0.pivot) {
newPivotInfo = {
...newPivotInfo,
...buildBasedOnType(level1, newPivotInfo)
}
if (level1.pivot) {
for (const level2 of level1.pivot) {
newPivotInfo = {
...newPivotInfo,
...buildBasedOnType(level2, newPivotInfo)
}
if (level2.pivot) {
for (const level3 of level2.pivot) {
newPivotInfo = {
...newPivotInfo,
...buildBasedOnType(level3, newPivotInfo)
}
}
}
}
}
}
}
accumulatedPivotInfo.push(newPivotInfo)
}
console.log(accumulatedPivotInfo)
}
function buildBasedOnType(level, newPivotInfo) {
if (level.field === 'pivotvalue1') {
level.value === 'Y' ? newPivotInfo['pivotvalue1.1'] = level.count : newPivotInfo['pivotvalue1.1'] = 0
level.value === 'N' ? newPivotInfo['pivotvalue1.2'] = level.count : newPivotInfo['pivotvalue1.2'] = 0
level.value === 'Z' ? newPivotInfo['pivotvalue1.3'] = level.count : newPivotInfo['pivotvalue1.3'] = 0
} else if (level.field === 'pivotvalue2' || level.field === 'pivotvalue3') {
newPivotInfo[level.field + 's'] === undefined ? newPivotInfo[level.field + 's'] = new Set([level.value]) : newPivotInfo[level.field + 's'].add(level.value)
} else {
newPivotInfo[level.field] = level.value
}
return newPivotInfo
}
这是我的最终输出结果以及我要实现的目标
pivotInfo = [
{
pivotvalue0: '111111111',
'pivotvalue1.1': 0,
'pivotvalue1.2': 0,
'pivotvalue1.3': 9,
pivotvalue2s: Set { 'Header1', 'Header2' },
pivotvalue3s: Set { 'Value1', 'Value2' }
},
{
pivotvalue0: '222222222',
'pivotvalue1.1': 23,
'pivotvalue1.2': 0,
'pivotvalue1.3': 0,
pivotvalue2s: Set { 'Header1', 'Header2' },
pivotvalue3s: Set { 'Value2' }
}
]
出于某种原因,代码运行器确实可以很好地处理集合。所以这是 jsfiddle 中设置集合的相同代码:https://jsfiddle.net/x9sa8tqm/
希望这是足够的上下文来了解我目前在做什么。现在我有数据可以通过值数组了解这些对象的深度。所以我可以检查数组长度并知道我需要走多深。使这种动态化的最佳方法是什么?所以它会弯曲到 'n' 深度而不是头部编码 4.
谢谢!
使用递归实现。
var data = [
{
field: 'pivotvalue0',
value: '200275399',
count: 36,
pivot: [{
field: 'pivotvalue0.1',
value: '200275399',
count: 36,
pivot: [{
field: 'pivotvalue0.2',
value: '200275399',
count: 36,
pivot: []
}]
}]
},
{
field: 'pivotvalue0',
value: '200746617',
count: 23,
pivot: []
}
]
var finalResult = [];
function accumulateValue(data){
data.forEach(item=>{
finalResult.push({
field: item.field,
value: item.value,
count: item.count,
});
if(item.pivot && item.pivot.length){
accumulateValue(item.pivot)
}
})
}
accumulateValue(data)
console.log(finalResult);
这是一种递归执行此操作的方法。我们 special-case 您想要专门处理的类型,对于所有其他情况,我们将 {[field]: value}
添加到我们的 运行ning 输出 object.
要注意的主要事情是递归是自动的。我们不手动编码关卡,这应该让我们可以任意深入。
// utility function
const gather = (prop) => (fn) => (o) =>
[... fn (o), ... (prop in o ? o [prop] : []) .flatMap (gather (prop) (fn))]
// helper function
const collectField = (name) => (o) => ({
[name + 's'] :
[... // comment out to get an actual set
new Set(
gather ('pivot') (
({field, value}) => field == name ? [value] : []
) (o)
)
] // comment out to get an actual set
})
// main function
const convert = (xs) =>
xs .map ((x, _, __, {field, value, count, pivot = []} = x) => Object .assign ({
...(
field == 'pivotvalue1'
? {
'pivotvalue1.1': value == 'Y' ? count: 0,
'pivotvalue1.2': value == 'N' ? count: 0,
'pivotvalue1.3': value == 'Z' ? count: 0,
}
: field == 'pivotvalue2' || field == 'pivotvalue3'
? {}
: {[field]: value}
)},
... convert (pivot),
collectField ('pivotvalue2') (x),
collectField ('pivotvalue3') (x),
))
// sample data
const topLevelObjects = [{field: "pivotvalue0", value: "111111111", count: 36, pivot: [{field: "pivotvalue1", value: "Y", count: 27, pivot: [{field: "pivotvalue2", value: "Header1", count: 27, pivot: [{field: "pivotvalue3", value: "Value1", count: 14}, {field: "pivotvalue3", value: "Value2", count: 13}]}]}, {field: "pivotvalue1", value: "Z", count: 9, pivot: [{field: "pivotvalue2", value: "Header2", count: 9, pivot: [{field: "pivotvalue3", value: "Value1", count: 9}]}]}]}, {field: "pivotvalue0", value: "222222222", count: 23, pivot: [{field: "pivotvalue1", value: "Y", count: 23, pivot: [{field: "pivotvalue2", value: "Header1", count: 12, pivot: [{field: "pivotvalue3", value: "Value2", count: 12}]}, {field: "pivotvalue2", value: "Header2", count: 11, pivot: [{field: "pivotvalue3", value: "Value2", count: 11}]}]}]}]
// demo
console .log (convert (topLevelObjects))
.as-console-wrapper {max-height: 100% !important; top: 0}
gather
是一个相当通用的辅助函数,用于递归地收集将函数应用于每个节点的结果。初始参数 prop
用于 属性 保存树的更深层的节点的名称(最常见的是 'children'
,这里是 'pivot'
。)第二个参数是一个函数,其每个节点的结果将在最后收集到一个数组中。当然,第三个参数是你的树 object 本身。这是我在这里使用的常用函数的轻微变体,因为它在将每次调用的结果包含在数组中之前将其展平。这允许提供给 return 具有单个值或没有值的数组的函数,并且输出数组仅包含实际值。 (如果我们想使用更通用的 gather
,有很多替代方法可以做这样的事情。)
我们在collectField
中使用gather
来获取所有关联的value
节点,当field
属性 匹配提供的 name
。请注意,我们不会在下面函数的主要递归调用中 运行 this 。这些结果将在稍后附上。虽然很可能通过深度递归调用来传递状态信息,但如果可以的话,我宁愿避免它。这意味着对于 pivotvalue2
和 pivotvalue3
,我们实际上进行了单独的递归。另请注意,您在此处的输出中请求 Set
,我使用数组是为了更轻松地进行演示。但我注意到您可以注释掉这两行以使其保持实际设置。
main函数将指定的字段收集到一个object中,然后在pivot
中对children进行递归调用,然后将这两个调用添加到[=19] =].
这对于性能来说并不理想,因为我们对输入进行多次递归遍历。但它是更简单的代码,如果存在可衡量的性能问题,我们可以稍后再回来。
(稍后我可能会考虑另一种方法,因为我有一个想法,但现在没有时间。)