JavaScript — Группируйте CSV-поля на основе пустых ячеек

#javascript #arrays #algorithm #csv #javascript-objects

Вопрос:

У меня есть данные, полученные из CSV-файла, которые могут содержать пустые поля и должны быть сгруппированы на основе этого.

Вот простой пример данных о лицах по местоположению. Данные о местоположении отображаются только для первого лица, и каждый следующий человек, которому не назначено местоположение, принадлежит местоположению, которое было указано ранее:

 "Objects:", [{
    location: "New York",
    date: "10/10/2021",
    name: "Max",
    surname: "Payne"
}, {
    location: "",
    date: "",
    name: "Duke",
    surname: "Nuken"
}, {
    location: "",
    date: "",
    name: "Jack",
    surname: "Carver"
}, {
    location: "Las Vegas",
    date: "30/10/2021",
    name: "Leon",
    surname: "Kennedy"
}, {
    location: "",
    date: "",
    name: "Donkey",
    surname: "Kong"
}, {
    location: "",
    date: "",
    name: "Ryu",
    surname: "Hayabusa"
}]
 

Мне нужно объединить список людей в список и добавить его в соответствующее место:

 "Objects:", [{
    location: "New York",
    date: "10/10/2021",
    persons: [{
        name: "Max",
        surname: "Payne"
    }, {
        name: "Duke",
        surname: "Nuken"
    }, {
        name: "Jack",
        surname: "Carver"
    }]
}, {
    location: "Las Vegas",
    date: "30/10/2021",
    persons: [{
        name: "Leon",
        surname: "Kennedy"
    }, {
        name: "Donkey",
        surname: "Kong"
    }, {
        name: "Ryu",
        surname: "Hayabusa"
    }]
}]
 

Данные первоначально поступают в виде массива массивов в этой форме:

 const [headers, ...lines] = [
    ["location",  "date",       "name",   "surname" ],
    ["New York",  "10/10/2021", "Max",    "Payne"   ],
    ["",          "",           "Duke",   "Nuken"   ],
    ["",          "",           "Jack",   "Carver"  ],
    ["Las Vegas", "30/10/2021", "Leon",   "Kennedy" ],
    ["",          "",           "Donkey", "Kong"    ],
    ["",          "",           "Ryu",    "Hayabusa"],
];
 

Код, который я в настоящее время должен преобразовать массив в первый список объектов в этом вопросе, таков:

 const locations = lines.map( (line) =>
   line.reduce((object, value, index) =>
        ({...object, [ headers[index] ]: value})
      , {}
   ));
 

Каков был бы хороший способ решить эту проблему?

Ответ №1:

вот пример использования Array#reduce() и жесткого кодирования имен заголовков.

 const
  [headers, ...lines] = [["location", "date", "name", "surname"], ["New York", "10/10/2021", "Max", "Payne"], ["", "", "Duke", "Nuken"], ["", "", "Jack", "Carver"], ["Las Vegas", "30/10/2021", "Leon", "Kennedy"], ["", "", "Donkey", "Kong"], ["", "", "Ryu", "Hayabusa"],],

  result = lines.reduce((a, [location, date, name, surname]) => {
    if (location amp;amp; date) a.push({ location, date, persons: [] });

    a[a.length - 1].persons.push({ name, surname });

    return a;
  }, []);

console.log(JSON.stringify(result, null, 2)); 
 .as-console-wrapper { max-height: 100% !important; top: 0; } 

Вот более динамичный пример, позволяющий указать «Требуемые объекты», которые должны присутствовать для группировки. Вы можете приспособиться к вашим конкретным потребностям.

 const
  input = [["location", "date", "name", "surname"], ["New York", "10/10/2021", "Max", "Payne"], ["", "", "Duke", "Nuken"], ["", "", "Jack", "Carver"], ["Las Vegas", "30/10/2021", "Leon", "Kennedy"], ["", "", "Donkey", "Kong"], ["", "", "Ryu", "Hayabusa"],],

  refactorCSV = (arr, requiredIndeces) => {
    const
      reduceByRequiredIndex = (arr) => arr.reduce((a, x, i) =>
        (a[ requiredIndeces.includes(i)].push(x), a), [[], []]),
      [headers, ...lines] = arr,
      [entryProps, requiredProps] = reduceByRequiredIndex(headers);

    return lines.reduce((a, line) => {
      const [personValues, requiredValues] = reduceByRequiredIndex(line);

      if (requiredValues.every(v => v !== '')) {
        const required = {};
        requiredProps.forEach((prop, i) => required[prop] = requiredValues[i]);

        a.push({ ...required, persons: [] });
      }

      const person = {};
      entryProps.forEach((prop, i) => person[prop] = personValues[i]);

      a[a.length - 1].persons.push(person);

      return a;
    }, []);
  },

  result = refactorCSV(input, [0, 1]);

console.log(JSON.stringify(result, null, 2)); 
 .as-console-wrapper { max-height: 100% !important; top: 0; } 

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

1. Это очень хорошо! К сожалению, в моем случае использования я считаю, что заголовки могут отличаться, например, у меня может быть дата рождения или возраст человека или другое поле в городе.