Добавьте динамические столбцы с помощью xlsx/sheetjs

У меня есть массив из нескольких тегов с идентификаторами и данными:

[
  {
    "id": "tagID1", 
    "error": { "code": 0, "success": true }, 
    "data": [
      [1604395417575, 108, 3], 
      [1604395421453, 879, 3]
    ]
  },
  {
    "id": "tagID2", 
    "error": {"code": 0, "success": true}, 
    "data": [
      [1604395417575, 508, 3], 
      [1604395421453, 179, 3]
    ]
  }
]

Я хочу преобразовать эти данные в электронную таблицу Excel с помощью пакета xlsx из NPM.

Примечание: 1604395417575 — это метка времени, 608 — это значение, 3 — это качество.

Я хочу показать в листе Excel, как в формате ниже

| Timestamp    |  tagID1 value  | tagID1 quality | tagID2 value | tagID2 quality|
| --------     | -------------- | -------- ------| -------------| ------------- |
| 1604395417575| 108            |   3            |     508      |   3           |
| 1604395421453| 879            |   3            |     179      |   3           |
    

Обновить формат листа

|----------------------------------------------------------
| Timestamp    |     TagID-1          |   TagID-2         | 
|              | ----------------------------------------
|              | value   | quality    | value   | quality |  
|----------------------------------------------------------
| 1604395417575|  108    |     3     |  508     |   3     |
| 1604395421453|  879    |     3     |  179     |   3     |

Я новичок в XLSX (он же SheetJS) — как мне это сделать?


person Mitul Panchal    schedule 10.04.2021    source источник
comment
Для ясности вы можете выровнять данные образца с ожидаемым выходом Excel?   -  person Robin Mackenzie    schedule 11.04.2021
comment
Несмотря на кучу правок, данные в массиве по-прежнему не совпадают с данными в примере формата Excel...   -  person Robin Mackenzie    schedule 12.04.2021
comment
да, смотрите мой ответ ниже   -  person Robin Mackenzie    schedule 13.04.2021
comment
@RobinMackenzie: Большое спасибо за помощь!!, всего один вопрос на тот случай, если мы хотим отформатировать мою обновленную таблицу Excel - 2, как я могу это сделать??   -  person Mitul Panchal    schedule 14.04.2021
comment
Привет - я ответил на ваш первоначальный вопрос с рабочим кодом. Если вы считаете, что он предлагает рабочее решение, вы можете проголосовать и принять его в качестве ответа. Плохой этикет расширять вопрос после того, как был дан ответ. В этом случае вы можете свободно голосовать и принимать, а затем задавать второй вопрос вроде «как мне объединить заголовки в SheetJS» или что-то подобное.   -  person Robin Mackenzie    schedule 14.04.2021
comment
@RobinMackenzie, да, действительно, он предложил рабочее решение, и я проголосовал и принял ответ Еще раз спасибо :)   -  person Mitul Panchal    schedule 15.04.2021
comment
@RobinMackenzie - можете ли вы помочь с этой проблемой xlsx читать лист по заголовку столбца"> stackoverflow.com/questions/67867980/   -  person Mitul Panchal    schedule 07.06.2021


Ответы (1)


В приведенном ниже коде выполняется следующий процесс:

  1. Преобразуйте данные, упорядочив свойства id и data каждого объекта в длинный список.
  2. Добавьте свойство order, которое представляет собой число в конце id, например. 1 для tagID1
  3. Отсортируйте этот новый массив по Timestamp, затем по order — это может быть ненужным, если ваши данные уже в таком порядке.
  4. Разберите заголовки и создайте пары tagIDN quality и tagIDN value
  5. Преобразуйте данные в широкий формат, взяв уникальные временные метки и создав 1 строку для каждой временной метки с таким количеством пар столбцов, сколько имеется тегов.
  6. Шаги 4 и 5 создают массив массивов, который можно передать методу XLSX XLSX.utils.aoa_to_sheet
  7. Поскольку эти длинные метки времени будут преобразованы Excel в экспоненциальное представление, установите для них числовой формат 0.
  8. Создайте книгу, вставьте лист методом из шага 6 и сохраните

Рабочий код:

const XLSX = require("xlsx");

// input data
const input_data = [
  {
    "id": "tagID1", 
    "error": { "code": 0, "success": true }, 
    "data": [
      [1604395417575, 108, 3], 
      [1604395421453, 879, 3]
    ]
  },
  {
    "id": "tagID2", 
    "error": {"code": 0, "success": true}, 
    "data": [
      [1604395417575, 508, 3], 
      [1604395421453, 179, 3]
    ]
  }
];

// data transforms
// 1st transform - get long array of objects
const prep = input_data.map(obj => {
  return obj.data.map(arr => {
    return {
      "TimeStamp": arr[0],
      "id": obj.id,
      "order": +obj.id.substr(5, obj.id.length - 5),
      "quality": arr[1],
      "value": arr[2]
    }
  });
}).flat();

// sort by timestamp asc, order asc
prep.sort((a, b) => a.TimeStamp - b.TimeStamp || a.order - b.order);

// headers
const headers = ["Timestamp"].concat(
  [...new Set(prep.map(obj => obj.id))]
    .map(id => [`${id} quality`, `${id} value`])
    .flat()
);

// organise the data - in wide format
const ws_data = [...new Set(prep.map(obj => obj.TimeStamp))]
  .map(ts => {
    const objByTimestamp = prep.filter(obj => obj.TimeStamp === ts);
    let arr = [ts];
    objByTimestamp.forEach(obj => arr = arr.concat([obj.quality, obj.value]));
    return arr;
  });

// prepend the headers
ws_data.unshift(headers);

// to Excel
// new workbook
const wb = XLSX.utils.book_new();

// create sheet with array-of-arrays to sheet method
const ws = XLSX.utils.aoa_to_sheet(ws_data);

// assign sheet to workbook
XLSX.utils.book_append_sheet(wb, ws, "Sheet1");

// set column A as text
const range = XLSX.utils.decode_range(ws['!ref']);
console.log(range);
for (let i = range.s.r; i <= range.e.r; i++) {
  const ref = XLSX.utils.encode_cell({r: i , c: 0});
  console.log(ref);
  ws[ref].z = "0";
}

// save workbook
XLSX.writeFile(wb, "C:\\Users\\Robin\\Desktop\\so.xlsx", {});

Вывод Excel:

введите здесь описание изображения

Редактировать

Чтобы иметь двойные заголовки с объединенными ячейками в первой строке (для идентификаторов тегов) - см. Обновление:

const XLSX = require("xlsx");

// input data
const input_data = [
  {
    "id": "tagID1", 
    "error": { "code": 0, "success": true }, 
    "data": [
      [1604395417575, 108, 3], 
      [1604395421453, 879, 3]
    ]
  },
  {
    "id": "tagID2", 
    "error": {"code": 0, "success": true}, 
    "data": [
      [1604395417575, 508, 3], 
      [1604395421453, 179, 3]
    ]
  }
];

// data transforms
// 1st transform - get long array of objects
const prep = input_data.map(obj => {
  return obj.data.map(arr => {
    return {
      "TimeStamp": arr[0],
      "id": obj.id,
      "order": +obj.id.substr(5, obj.id.length - 5),
      "quality": arr[1],
      "value": arr[2]
    }
  });
}).flat();

// sort by timestamp asc, order asc
prep.sort((a, b) => a.TimeStamp - b.TimeStamp || a.order - b.order);

// headers
// const headers = ["Timestamp"].concat(
//   [...new Set(prep.map(obj => obj.id))]
//     .map(id => [`${id} quality`, `${id} value`])
//     .flat()
// );
const ids = [...new Set(prep.map(obj => obj.id))];
const headers1 = [""].concat(ids.map(id => Array(2).fill(id)).flat());
const headers2 = ["Timestamp"].concat(ids.map(id => Array(["quality", "value"])).flat()).flat();

// organise the data - in wide format
const ws_data = [...new Set(prep.map(obj => obj.TimeStamp))]
  .map(ts => {
    const objByTimestamp = prep.filter(obj => obj.TimeStamp === ts);
    let arr = [ts];
    objByTimestamp.forEach(obj => arr = arr.concat([obj.quality, obj.value]));
    return arr;
  });

// prepend the headers
ws_data.unshift(headers2);
ws_data.unshift(headers1);

// to Excel
// new workbook
const wb = XLSX.utils.book_new();

// create sheet with array-of-arrays to sheet method
const ws = XLSX.utils.aoa_to_sheet(ws_data);

// assign sheet to workbook
XLSX.utils.book_append_sheet(wb, ws, "Sheet1");

// set column A as text
const range = XLSX.utils.decode_range(ws['!ref']);
for (let i = range.s.r; i <= range.e.r; i++) {
  const ref = XLSX.utils.encode_cell({r: i , c: 0});
  ws[ref].z = "0";
}

// assign merges to sheet
// https://stackoverflow.com/questions/53516403/sheetjs-xlsx-how-to-write-merged-cells
const merges = ids.reduce((acc, curr, idx) => {
  acc.push({
    s: {r: 0, c: 1 + (2 *idx)},
    e: {r: 0, c: 1 + (2 *idx) + 1}
  });
  return acc;
}, []);
ws["!merges"] = merges;

// save workbook
XLSX.writeFile(wb, "C:\\Users\\Robin\\Desktop\\so.xlsx", {});

Вывод Excel:

введите здесь описание изображения

Метод описан в этом post.

person Robin Mackenzie    schedule 13.04.2021
comment
Спасибо Робин!! просто вопрос, как я могу объединить заголовки в SheetJS, было бы очень полезно - person Mitul Panchal; 15.04.2021
comment
@MitulPanchal - проверьте обновление в ответе - показывает объединенные ячейки в первой строке заголовка - person Robin Mackenzie; 15.04.2021