Объединение элементов из двух массивов на основе совпадающего идентификатора - PullRequest
3 голосов
/ 16 апреля 2019

У меня есть такой объект данных:

{
  "data1": [
    [
      "ID",
      "name",
      "Birthday"
    ],
    [
      "10",
      "thomas",
      "1992-03-17"
    ],
    [
      "11",
      "Emily",
      "2000-03-03"
    ]
  ],
  "data2": [
    [
      "Balance",
      "ID"
    ],
    [
      "$4500",
      "10"
    ],
    [
      "$1500",
      "13"
    ]
  ]
}

Содержит два массива data1 и data2. Первая строка в каждом массиве - это имя столбцов, а остальные строки содержат данные (представьте их как таблицу).

Я хочу сравнить поле ID в обоих массивах, и если совпадение ID s, то в конечном выводе будет столбец Balance с балансом, соответствующим этому ID, и если ID s не совпадают, тогда Balance будет $0.

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

{
  "output": [
    [
      "ID",
      "name",
      "Birthday",
      "Balance"
    ],
    [
      "10",
      "thomas",
      "1992-03-17",
      "$4500" //ID 10 matched so the balance added here
    ],
    [
      "11",
      "Emily",
      "2000-03-03",
      "0" //0 bcoz the ID 11 is not there in data2 array
    ]
  ]

}

Я считаю, что это сложно сделать. Думайте об этом как о левом соединении в MySQL. Я ссылался на это решение , но в моем случае оно не работает, так как в моем ответе нет ключей.

РЕДАКТИРОВАТЬ : Мне также нужно присоединиться к другим полям.

Ответы [ 4 ]

2 голосов
/ 16 апреля 2019

Вы можете использовать Array.prototype.map () , найти , фильтр , срез , уменьшить , concat , включает и Object.assign () .

Это решение:

  • Обрабатывает произвольный порядок элементов. Заказ читается из заголовков.
  • Добавляет поле Balance, только если в data2 присутствует один элемент.
  • Объединяет все остальные поля (запрошено OP, см. Комментарии ниже) .
  • Принимает значения по умолчанию в качестве входных данных и использует их, если данные отсутствуют в data1 и data2.

function merge({ data1, data2 }, defaults) {
  // get the final headers, add/move 'Balance' to the end
  const headers = [...data1[0].filter(x => x !== 'Balance')]
    .concat(data2[0].includes('Balance') ? ['Balance'] : []);
  
  // map the data from data1 to an array of objects, each key is the header name, also merge the default values.
  const d1 = data1.slice(1)
    .map(x => x.reduce((acc, y, i) => ({ ...defaults, ...acc, [data1[0][i]]: y }), {}));
  // map the data from data2 to an array of objects, each key is the header name
  const d2 = data2.slice(1)
    .map(x => x.reduce((acc, y, i) => ({ ...acc, [data2[0][i]]: y }), {}));
  
  // combine d1 and d2
  const output = d1.map((x, i) => { // iterate over d1
    // merge values from d2 into this value
    const d = Object.assign(x, d2.find(y => y['ID'] === x['ID']));
    // return an array ordered according to the header
    return headers.map(h => d[h]);
  });
  return { output: [headers, ...output] };
}

const test0 = {
  data1: [[ "ID","name","Birthday","other"],["10","thomas","1992-03-17","empty"],["11","Emily","2000-03-03","empty"]],
  data2: [["other", "ID", "Balance", "city"],["hello", "10", "$4500", "New York"],["world", "10","$8","Brazil"]]
};

const test1 = {
  data1: [["ID","name","Birthday"],["10","thomas","1992-03-17"],["11","Emily","2000-03-03"]],
  data2: [["other","ID"],["x","10"],["y","11"]]
};

console.log(merge(test0, { Balance: '$0' }));
console.log(merge(test1, { Balance: '$0' }));
1 голос
/ 16 апреля 2019
const KEY_ID = "ID";

var data = {
  "data1": [
    [ "ID", "name", "Birthday" ],
    [ "10", "thomas", "1992-03-17" ],
    [ "11", "Emily", "2000-03-03" ]
  ],
  "data2": [
    [ "Balance", "ID" ],
    [ "$4500", "10" ],
    [ "$1500", "13" ]
  ]
}

var merged = Object.keys(data).map(function (key) {
  var tmp = data[key].slice();
  var heads = tmp.shift();
  return tmp.map(function (item) {
    var row = {};
    heads.forEach(function (head, i) {
      row[head] = item[i];
    });
    return row;
  });
}).flat().reduce(function (acc, row) {
  var found = acc.find(function (item) {
    return row[KEY_ID] === item[KEY_ID];
  })
  if (!found) {
    found = row;
    acc.push(found);
  } else {
    Object.keys(row).forEach(function (head) {
      found[head] = row[head];
    });
  }
  return acc;
}, []);

console.log(merged);

Это решение является масштабируемым: если вы добавите свойства, оно масштабирует новый формат.

0 голосов
/ 16 апреля 2019
let a = { "data1": [ ... ],"data2": [ ...] }
let r = a.data1.reduce((r,u,i)=>{
  if(i !== 0)
  {
    let entry = a.data2.filter((a)=> a[1]===u[0])
    r.push([...u,entry.length ? entry[0][0] : 0])
  }
   return r
},[[
      "ID",
      "name",
      "Birthday",
      "Balance"
]])


0 голосов
/ 16 апреля 2019

Вы можете абстрагировать все табличные операции в классоподобные:

 function Table(array) {
   const [head, ...values] = array;

  const Entry =(entry) => ({
   get(key) { return entry[ head.indexOf(key) ]; },
   set(key, value) { entry[ head.indexOf(key) ] = value; }
  });

  return {
    index(name) {       
      const result = {};
      for(const value of values)
        result[ value[ head.indexOf(name) ] ] = Entry(value);

      return result;
    },
    *[Symbol.iterator]() {
      for(const row of values)
         yield Entry(row);
     },

      addRow(key) { head.push(key); }
   };
 }

Используется как:

 const users = Table(obj.data1);
 const balances = Table(obj.data2);

 const balanceByID = balance.index("ID");

 users.addRow("Balance");

 for(const user of users)
   user.set("Balance", balanceByID[ user.get("ID") ].get("Balance"));
...