Объедините 2 списка в один, используя функцию карты и стрелки - PullRequest
4 голосов
/ 14 февраля 2020

У меня есть 2 списка, и я хочу объединить их, чтобы заполнить их списком. Я знаю, что это можно сделать с помощью вложенных циклов for, но я стараюсь избегать циклов for из-за объема данных, который мне потребуется для l oop on. Я хотел бы добиться этого, используя функции стрелок или что-то еще.

Список первый:

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

Как мне этого добиться?

Ответы [ 3 ]

2 голосов
/ 22 февраля 2020
  1. Вам нужно беспокоиться не о циклах, а о сложности алгоритма. Как я вижу, у вас есть дополнительные поля в rows, и вы не запрашивали null значений в желаемом выводе. Итак, я бы предложил решение, отличное от решения, предложенного Кристосом Литрой.
    Итерация по 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],
            }))
        )
    }
    
  2. И, кстати,

    Я знаю, что это можно сделать с помощью вложенных циклов for, но я я пытаюсь избежать циклов из-за объема данных, которые мне понадобятся l oop для функции

    ... даже в современных браузерах циклы превосходят по производительности map(), reduce() и Ко, а не наоборот.

    Посмотрите на время во фрагменте. По крайней мере, в моей среде 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
2 голосов
/ 20 февраля 2020

Свойство 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))
);
0 голосов
/ 25 февраля 2020

Я немного изменил код и удалил условную проверку, если поле в контакте существует, так как 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
    }))
  )
}
...