使用地图和箭头功能将 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>

  1. 您应该担心的不是循环,而是算法的复杂性。如我所见,您在 rows 中有可选字段,并且您没有在所需输出中要求 null 值。所以,我会提出一个不同于 Christos Lytras 的解决方案。
    在每一行迭代中迭代 fields 会给你带来 O(N^M) 的复杂性。其中 N - 是 rows.lengthMfields.length。这可能是个坏主意。下面的代码会给你线性复杂度O(N+M)。其中 M 仍然是 fields.lengthNrows 每行字段数的总和,这听起来比 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],
            }))
        )
    }
    
  2. 顺便说一句

    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
    }))
  )
}