使用地图和箭头功能将2合并为一个列表

问题描述 投票:4回答:3

我有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
3个回答
2
投票

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>

2
投票
  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. 顺便说一句

    我知道这可以使用嵌套的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 })) ) }


0
投票
我更改了一些代码,并删除了条件检查联系中是否存在字段,因为javascript默认会返回undefined
© www.soinside.com 2019 - 2024. All rights reserved.