Обновить массив атрибутами из другого массива с одинаковым ключом для обоих — javascript

#javascript #node.js #arrays #ecmascript-6

#javascript #node.js #массивы #ecmascript-6

Вопрос:

У меня есть следующий массив, который считается большим набором данных.

 let response1 = [
  { userID: '2222', dataOne: [ [Object], [Object] ] },
  {
    userID: '6666',
    dataOne: [ [Object] ],
    dataTwo: [ [Object], [Object] ]
  },
  {
    userID: '11111',
    dataOne: [ [Object], [Object] ],
    dataTwo: [ [Object] ]
  },
  { userID: '4586', dataTwo: [ [Object] ] }
];
  

У меня есть другой массив, который я получил в результате запроса к базе данных (который также представляет собой большой набор данных)

 let dbResponse  = [{
  "attributes": {
    "dob": "19890147",
    "gender": "M",
    "mobilePhone": "1239000000",
    "name": "Ketan Hol",
  },
  "doctorID": "ds45ds",
  "userID": "11111"
},
{
  "attributes": {
    "dob": "19890386",
    "gender": "M",
    "mobilePhone": "1239000000",
    "name": "Sachin",
  },
  "doctorID": "erjjkrel",
  "userID": "6666"
},
{
  "attributes": {
    "dob": "19890219",
    "gender": "M",
    "mobilePhone": "1239000000",
    "name": "Vishwas",
  },
  "doctorID": "dfgfdg",
  "userID": "2222"
},
{
  "attributes": {
    "dob": "19890219",
    "gender": "M",
    "mobilePhone": "1239000000",
    "name": "Jis",
  },
  "doctorID": "dfgfdg",
  "userID": "98645"
},
{
  "attributes": {
    "dob": "19890219",
    "gender": "M",
    "mobilePhone": "1239000000",
    "name": "Brad",
  },
  "doctorID": "dfgfdg",
  "userID": "4586"
},
    {
          "attributes": {
            "dob": "19890219",
            "gender": "M",
            "mobilePhone": "1239000000",
            "name": "Brad",
          },
          "doctorID": "dfgfdg",
          "userID": "4586"
        }

];
  

Мне нужно добавить такие атрибуты, как dob, name из dbResponse в массив response1 на основе того же идентификатора пользователя.
Все идентификаторы пользователя в массиве response1 должны быть заполнены атрибутами, такими как dob, name из dbResponse. Я не понимаю, как выполнить приведенное ниже в большом наборе данных.

Ожидаемый результат будет таким:

 response1 = [
      { userID: '2222', dataOne: [ [Object], [Object] ], dob: '19890219', name: 'Vishwas' },
      {
        userID: '6666',
        dataOne: [ [Object] ],
        dataTwo: [ [Object], [Object] ],
        dob: '19890386',
        name: 'Sachin'
      },
      {
        userID: '11111',
        dataOne: [ [Object], [Object] ],
        dataTwo: [ [Object] ],
        dob: '19890147',
        name: 'Ketan Hol'
      },
      { userID: '4586', dataTwo: [ [Object] ], dob: '19890219', name: 'Brad' }
    ];
  

Каков будет наилучший способ добиться этого с использованием функций es6 для больших наборов данных? Я новичок в этих функциях es6. Любая помощь была бы очень признательна.

Ответ №1:

Подход1

Выполните итерацию dbResponse для каждого userId элемента response1 , извлеките объект и скопируйте объект в response1 .

Подход2 (оптимизированная операция)

Поскольку оба являются большими массивами, вам придется выполнять итерацию dbResponse большое количество раз. Чтобы оптимизировать операцию поиска response1 соответствующего userID объекта в dbResponse массиве, вы могли бы сохранить сопоставление, чтобы уменьшить сложность поиска.

 const result = dbResponse.reduce((acc, obj) => {
    const { userID } = obj
    acc[userID] = obj;
    return acc;
}, {});
const finalResult = response1.reduce((acc, curr) => {
    const { userID } = curr
    const dbObj = result[userID] || {}
    acc.push({
        ...curr,
        ...dbObj
    })
    return acc;
}, []);
  

Конечный результат будет в finalResult

Комментарии:

1. Спасибо, но мне не нужен MobilePhone amp; DoctorID, так как мы можем улучшить то же самое?

2. Есть ли какие-либо проблемы при преобразовании в это? const результат = dbResponse.reduce((acc, { атрибуты: { dob, имя }, идентификатор пользователя }) => { acc[Идентификатор пользователя] = { dob, имя, идентификатор пользователя}; вернуть acc; }, {});

3. Вы разрушили свойства текущего объекта. И finalResult не будет содержать никаких данных в attributes ключе, поскольку вы добавили их отдельно. Это будет работать правильно.

4. Просто предложение, добавьте значения по умолчанию. Например: если attributes ключ не существует в каком-либо объекте, код сломается.

5. Вы имели в виду, проверяя условие if? или любой другой хороший метод, который у нас есть?

Ответ №2:

Итак, я проверял это, ответ @mappie довольно хорош, индексированный способ — самый быстрый способ, хотя окончательный результат может быть выполнен с использованием map, а не reduce, лично карта вещей понятнее и кажется похожей по времени. Вы можете видеть, что использование Find действительно медленное по сравнению с другими 2 решениями.

Здесь вы можете найти фрагмент, сравнивающий временные интервалы между методами. решение @mappie, но с использованием Map (но нет проверки на несоответствие между 2 массивами)

 const result = dbResponse.reduce((acc, obj) => {
    const { userID } = obj;
    acc[userID] = obj;
    return acc;
  }, {});
  const finalResult = response1.map((user) => ({
    ...user,
    ...result[user.userID].attributes,
  }));
  

 /******************************************************************************
 * Mock Data Creation tools
 *****************************************************************************/
const getRandomBetween = (start, end) =>
  Math.floor(Math.random() * (end - start))   start;
const getRandomDOB = () =>
  getRandomBetween(1950, 2000).toString()  
  getRandomBetween(10, 12).toString()  
  getRandomBetween(10, 29).toString();
const getRandomGender = () => (getRandomBetween(0, 1) === 0 ? "M" : "F");
const getUserIdGenerator = (state) => () => state.userID  ;

const getResponseDocument = (userIdGenerator) => ({
  userID: userIdGenerator(),
  dataOne: "don't care",
});
const getDbResponseDocument = (userIdGenerator) => ({
  attributes: {
    dob: getRandomDOB(),
    gender: getRandomGender(),
    mobilePhone: getRandomBetween(1000000000, 9999999999).toString(),
    name: "Ketan Hol",
  },
  doctorID: "ds45ds",
  userID: userIdGenerator(),
});

/******************************************************************************
 * Mock Data Creation
 *****************************************************************************/
const usersAmount = 50000;
const r1UidGen = getUserIdGenerator({ userID: 10000 });
const response1 = Array.from(Array(usersAmount).keys()).map(() =>
  getResponseDocument(r1UidGen)
);
const dbRUidGen = getUserIdGenerator({ userID: 10000 });
const dbResponse = Array.from(Array(usersAmount).keys()).map(() =>
  getDbResponseDocument(dbRUidGen)
);

/******************************************************************************
 * Different ways to merge the arrays
 *****************************************************************************/

function methodIndexed() {
  const result = dbResponse.reduce((acc, obj) => {
    const { userID } = obj;
    acc[userID] = obj;
    return acc;
  }, {});
  const finalResult = response1.reduce((acc, curr) => {
    const { userID } = curr;
    const dbObj = result[userID] || {};
    acc.push({
      ...curr,
      ...dbObj.attributes,
    });
    return acc;
  }, []);
  return finalResu<
}

function methodIndexedMap() {
  const usersById = dbResponse.reduce((acc, obj) => {
    const { userID } = obj;
    acc[userID] = obj;
    return acc;
  }, {});
  const finalResult = response1.map((user) => ({
    ...user,
    ...usersById[user.userID].attributes,
  }));
  return finalResu<
}

const byUserId = (userId) => (item) => item.userID === userId;
function methodFind() {
  return response1.map((user) => ({
    ...user,
    ...dbResponse.find(byUserId(user.userID)).attributes,
  }));
}
const results = [];

/******************************************************************************
 * Test Methods
 *****************************************************************************/
function testMethod(name, method) {
  const title = `Method: "${name}"`;
  console.time(title);
  const result = method();
  console.timeEnd(title);
  // assert data validity
  if (
    !(
      result[42].userID === response1[42].userID amp;amp;
      result[42].userID === dbResponse[42].userID amp;amp;
      result[42].dob === dbResponse[42].attributes.dob
    )
  ) {
    throw Error(`Method "${name}" does not produce expected output`);
  }
}

// difference between these two are too tight to V8 Runtime Optimizations
// so we run them a few time to stabilize
for (let i = 0; i < 10; i  ) {
  testMethod("Indexed", methodIndexed);
  testMethod("Indexed Map", methodIndexedMap);
}

testMethod("Using Find", methodFind);  
 .as-console-wrapper { max-height: 100% !important; top: 0; }  

Комментарии:

1. Вау, отличное объяснение и свежая пара глаз. Спасибо за ваше драгоценное время.

2. Да, map может использоваться вместо reduce . Хотя уменьшение дает свободу управлять вводом в накопитель, что map не так. С точки зрения производительности, reduce и map похожи. Один немного превосходит другой в зависимости от операции, выполняемой при обратном вызове.

Ответ №3:

Мы можем использовать Array.map для объединения двух наборов данных, также используя Array.найдите, чтобы сопоставить пользователей из массива response1 с массивом dbResponse.

Затем мы можем использовать Object.assign() для копирования всех свойств из атрибутов в dbUser в пользовательский объект.

 let response1 = [ { userID: '2222', dataOne: [ {}, {} ] }, { userID: '6666', dataOne: [ {} ], dataTwo: [ {}, {} ] }, { userID: '11111', dataOne: [ {}, {} ], dataTwo: [ {} ] }, { userID: '4586', dataTwo: [ {} ] } ];
let dbResponse  = [{ "attributes": { "dob": "19890147", "gender": "M", "mobilePhone": "1239000000", "name": "Ketan Hol", }, "doctorID": "ds45ds", "userID": "11111" }, { "attributes": { "dob": "19890386", "gender": "M", "mobilePhone": "1239000000", "name": "Sachin", }, "doctorID": "erjjkrel", "userID": "6666" }, { "attributes": { "dob": "19890219", "gender": "M", "mobilePhone": "1239000000", "name": "Vishwas", }, "doctorID": "dfgfdg", "userID": "2222" }, { "attributes": { "dob": "19890219", "gender": "M", "mobilePhone": "1239000000", "name": "Jis", }, "doctorID": "dfgfdg", "userID": "98645" }, { "attributes": { "dob": "19890219", "gender": "M", "mobilePhone": "1239000000", "name": "Brad", }, "doctorID": "dfgfdg", "userID": "4586" }, { "attributes": { "dob": "19890219", "gender": "M", "mobilePhone": "1239000000", "name": "Brad", }, "doctorID": "dfgfdg", "userID": "4586" }  ];

function mergeUserData(response, dbResponse) {
    return response.map(user => {
        // Find the same user in the dbResponse array 
        let dbUser = dbResponse.find(dbUser => dbUser.userID === user.userID);
        if (dbUser) {
            // Copy the relevant information 
            user.name = dbUser.attributes.name;
            user.dob = dbUser.attributes.dob;
        }
        return user;
    })
}
console.log("Merged data:", mergeUserData(response1, dbResponse));  
 .as-console-wrapper { max-height: 100% !important; top: 0; }  

Комментарии:

1. Спасибо, но мне не нужен мобильный телефон, поэтому я не могу напрямую передать сам атрибут, верно?

2. В этом случае мы можем просто скопировать соответствующие свойства!