【问题标题】:Combine 2 lists into one using map & arrow function使用地图和箭头功能将 2 个列表合二为一
【发布时间】:2020-02-14 08:25:43
【问题描述】:

我有 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"
            }
        ],
        [
            ...
        ],
        [
            ...
        ]
    ]
]

我怎样才能做到这一点?

【问题讨论】:

    标签: ecmascript-6 arrow-functions


    【解决方案1】:

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

    运行这段代码sn-p查看结果:

    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>

    【讨论】:

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

        我知道这可以使用嵌套的 for 循环来完成,但是我试图避免 for 循环,因为我必须在函数上循环大量数据

        ...即使在现代浏览器中,循环的性能也优于 map()reduce() 和 Co.,而不是相反。

        查看 sn-p 中的时间。至少在我的环境中,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

      【讨论】:

      • 感谢您的详细回答,在我发布此问题后,我确实运行了性能测试,对于我拥有的数据量,for 循环确实表现良好(稍微),但我仍然想如何使用地图来完成 :)
      【解决方案3】:

      我已经更改了一些代码并删除了条件检查是否存在联系人字段,因为默认情况下 javascript 将返回未定义

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

      【讨论】:

        猜你喜欢
        • 2013-12-21
        • 1970-01-01
        • 2015-01-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-07-07
        • 2023-03-10
        相关资源
        最近更新 更多