使用地图和箭头功能将 2 个列表合并为一个
Combine 2 lists into one using map & arrow function
我有 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
的每个元素,然后用 row
数据(如果存在)填充每个 field
数据。此外,我在 rows
数组内的任何行对象上都看不到 Id
字段。如果 field
不存在,此代码设置 null
:
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],
}))
)
}
顺便说一句
I know this can be done using nested for loops but, I'm trying to avoid for loops because of the amount of data I'll have to loop on function
...即使在现代浏览器中,循环的性能也优于 map()
、reduce()
和 Co.,而不是相反。
查看代码段中的计时。至少在我的环境中 for
版本比 map
版本快两倍(在第一个 运行 上)。当然,根据 JIT 编译器的标准,当时的代码绝不是 hot,因此代码还没有被浏览器优化。在 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 默认情况下 return 未定义
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
}))
)
}
我有 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
的每个元素,然后用 row
数据(如果存在)填充每个 field
数据。此外,我在 rows
数组内的任何行对象上都看不到 Id
字段。如果 field
不存在,此代码设置 null
:
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], })) ) }
顺便说一句
I know this can be done using nested for loops but, I'm trying to avoid for loops because of the amount of data I'll have to loop on function
...即使在现代浏览器中,循环的性能也优于
map()
、reduce()
和 Co.,而不是相反。查看代码段中的计时。至少在我的环境中
for
版本比map
版本快两倍(在第一个 运行 上)。当然,根据 JIT 编译器的标准,当时的代码绝不是 hot,因此代码还没有被浏览器优化。在 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 默认情况下 return 未定义
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
}))
)
}