我有2个列表,我想将它们组合在一起,以便可以将它们填充到列表中。我知道可以使用嵌套的for循环来完成此操作,但是由于要循环处理的数据量,我试图避免使用for循环。我想使用箭头功能或其他方法实现此目的。
清单一:
let fields = [
{
field: "Name",
fieldType: "Text"
},
{
field: "Active__c",
fieldType: "Boolean"
},
{
field: "Contact",
fieldType: "Relationship"
}
];
清单二:
let rows = [
{
contact: {
Name: "Joe",
Active__c: true,
Contact: "SomeContact"
}
},
{
contact: {
Name: "Rachel",
Active__c: true
}
},
{
contact: {
Name: "Ross",
Active__c: true
}
},
{
contact: {
Name: "Monica",
Active__c: true
}
}
];
当前代码:
let output = rows.map(row => ({
id: row.Id,
data: {
value: fields.map(field => (row.contact[field.field])),
field: fields.map(field => field.field)
}
}));
此代码的输出:
[
{
"data": {
"value": [
"Joe",
true,
"SomeContact"
],
"field": [
"Name",
"Active__c",
"Contact"
]
}
},
{
"data": {
"value": [
"Rachel",
true,
null
],
"field": [
"Name",
"Active__c",
"Contact"
]
}
},
{
"data": {
"value": [
"Ross",
true,
null
],
"field": [
"Name",
"Active__c",
"Contact"
]
}
},
{
"data": {
"value": [
"Monica",
true,
null
],
"field": [
"Name",
"Active__c",
"Contact"
]
}
}
]
所需的输出:
[
data : [
[
{
field : "Name",
type: "Text",
value : "Joe"
},
{
field : "Active__c",
type: "Boolean",
value : true
},
{
field : "Contact",
type: "Relationship",
value : "SomeContact"
}
],
[
{
field : "Name",
type: "Text",
value : "Rachel"
},
{
field : "Active__c",
type: "Boolean",
value : false
},
{
field : "Contact",
type: "Relationship",
value : "SomeContact Two"
}
],
[
...
],
[
...
]
]
]
我该如何实现?
data
属性是唯一的,必须内联定义以创建对象(不是所需输出中的数组)。您必须将fields
数组映射到rows
的每个元素,然后使用field
数据填充每个row
数据(如果存在)。另外,在Id
数组内的任何行对象上都看不到rows
字段。如果null
不存在,则此代码设置field
:
let output = {
data: rows.map(({ contact }) =>
fields.map(({ field, fieldType: type }) => ({
field,
type,
value: field in contact ? contact[field] : null // Set null if contact has no field
}))
)
}
运行此代码段以查看结果:
let fields = [
{
field: "Name",
fieldType: "Text"
},
{
field: "Active__c",
fieldType: "Boolean"
},
{
field: "Contact",
fieldType: "Relationship"
}
];
let rows = [
{
contact: {
Name: "Joe",
Active__c: true,
Contact: "SomeContact"
}
},
{
contact: {
Name: "Rachel",
Active__c: true
}
},
{
contact: {
Name: "Ross",
Active__c: true
}
},
{
contact: {
Name: "Monica",
Active__c: true
}
}
];
let output = {
data: rows.map(({ contact }) =>
fields.map(({ field, fieldType: type }) => ({
field,
type,
value: field in contact ? contact[field] : null
}))
)
}
document.getElementById('output').appendChild(
document.createTextNode(JSON.stringify(output, null, 2))
);
<pre id="output"></pre>
不是您应该担心的循环,而是算法的复杂性。如我所见,您在rows
中有可选字段,并且没有在所需的输出中要求null
值。因此,我提出了一种不同于Christos Lytras的解决方案。在每行迭代中对fields
进行迭代都会使您O(N^M)
变得复杂。其中N
-是rows.length
,M
是fields.length
。这可能是一个坏主意。以下代码将为您提供线性复杂度O(N+M)
。 M
仍然是fields.length
,N
是rows
每行中字段数的总和,听起来比O(N^M)
还要可怕,但是,如果您有可选字段,它将为您节省很多钱-在摘要输出中寻找called X times
。
// prepare dictionary, later on fields_dict[field] will have O(1) complexity
const fields_dict = fields.reduce((acc, {field, fieldType}) => {
acc[field] = fieldType
return acc
}, {})
let output2 = {
data: rows.map(({ contact }) =>
Object.keys(contact).map(field => ({ // iterate only over existing fields
field,
type: fields_dict[field],
value: contact[field],
}))
)
}
顺便说一句
我知道这可以使用嵌套的for循环来完成,但由于要在函数上循环的数据量,我试图避免for循环。>
...即使在现代浏览器中,循环的性能也优于
map()
,reduce()
和Co.,反之亦然。
看看片段中的时间。至少在我的环境中,for
版本是map
版本的两倍(第一次运行)。当然,此时的代码绝不是JIT编译器的标准,因此浏览器尚未对代码进行优化。在JIT编译之后,性能差异可以忽略不计(按Run code snippet
几次以查看)。尽管如此,循环更快
let fields = [
{ field: "Name" , fieldType: "Text" },
{ field: "Active__c", fieldType: "Boolean" },
{ field: "Contact" , fieldType: "Relationship" },
{ field: "extra1" , fieldType: "something" },
{ field: "extra2" , fieldType: "something" },
{ field: "extra3" , fieldType: "something" },
{ field: "extra4" , fieldType: "something" },
];
let rows = [
{ contact: { Name: "Joe" , Active__c: true, Contact: "SomeContact" } },
{ contact: { Name: "Rachel", Active__c: true } },
{ contact: { Name: "Ross" , Active__c: true } },
{ contact: { Name: "Monica", Active__c: true } },
{ contact: { Name: "Monica", Active__c: true } },
{ contact: { Name: "Monica", Active__c: true } },
{ contact: { Name: "Monica", Active__c: true } },
{ contact: { Name: "Monica", Active__c: true } },
{ contact: { Name: "Monica", Active__c: true } },
{ contact: { Name: "Monica", Active__c: true } },
];
let i
i = 0
console.time("Christos Lytras version")
let output1 = {
data: rows.map(({ contact }) =>
fields.map(({ field, fieldType: type }) => (i++,{
field,
type,
value: field in contact ? contact[field] : null
}))
)
}
console.timeEnd("Christos Lytras version")
console.log(`called ${i} times`)
i = 0
let j = 0
console.time("functional version")
const fields_dict = fields.reduce((acc, {field, fieldType}) => { i++, acc[field] = fieldType; return acc }, {})
let output2 = {
data: rows.map(({ contact }) => Object.keys(contact).map(field => (j++,{
field,
type: fields_dict[field],
value: contact[field],
})))
}
console.timeEnd("functional version")
console.log(`called ${i+j} times`)
i = 0
console.time("loop version")
const fields_dict2 = {}
for(const {field, fieldType} of fields) { i++; fields_dict2[field] = fieldType }
const output3 = { data: new Array(rows.length) }
j = 0
for(let r = 0 ; r !== rows.length ; ++r ) {
const contact = rows[r].contact
const contact_another_format = output3.data[r] = []
for(const field in contact) {
j++
contact_another_format.push({
field,
type: fields_dict2[field],
value: contact[field],
})
}
}
console.timeEnd("loop version")
console.log(`called ${i+j} times`)
// console.log(JSON.stringify(output1, undefined, 2))
// console.log(JSON.stringify(output2, undefined, 2))
console.log(JSON.stringify(output3, undefined, 2))
console.log("results are equal:", JSON.stringify(output2) === JSON.stringify(output3)) // intentionally not equal to output1
我更改了一些代码,并删除了条件检查联系中是否存在字段,因为javascript默认会返回undefined
let output = { data: rows.map(({ contact }) => fields.map(({ field, fieldType: type }) => ({ field, type, value: contact[field] // It wills et undefined if field key not present in contact })) ) }