Switch up row values in an array and transform them into an object using SheetJS

I am struggling to format an array where each "Working Day" is represented as an object with specific details like index and start/end date. I need help manipulating the JSON data to achieve the desired structure.

The package I'm currently using is: https://github.com/SheetJS/sheetjs

Here is my current code:

const workbook = XLSX.read(file.buffer, { type: 'buffer' });
const worksheet = workbook.Sheets['Calendar Config'];
const parsed = XLSX.utils.sheet_to_json(worksheet);

Current output looks like this:

[
  {
    'Calendar Name': 'Standard',
    'Valid From': 44197,
    'Valid To': 44561,
    'Use Holidays': 'yes',
    'Working Day': 'Monday',
    Start: 0.3333333333333333,
    End: 0.8333333333333334
  },
  {
    'Working Day': 'Tuesday',
    Start: 0.3333333333333333,
    End: 0.8333333333333334
  },
  ...
]

Desired JSON structure should be:

{
 "name": "Standard",
 "validFrom": "2021-01-01T00:00:00.000Z",
 "validTo": "2021-12-31T00:00:00.000Z",
 "useHolidays": true,
 "workingDays": [
  {
    "dayIndex": 0,
    "dayStart": "8:00",
    "dayEnd": "20:00"
  },
  ...
 ],
 "uploadedBy": "foo"
}

Example of the Parsed Excel Sheet:

https://i.sstatic.net/mCxXs.png

Answer №1

Imagine if your data in the spreadsheet was structured like this - processing each row without considering headers would make it easier to generate the intended results:

To achieve this, you can transform the sheet_to_json output accordingly as shown in this reference. Don't forget that sheet_to_json is utilized with {header: 1}:

const XLSX = require("xlsx");
const filename = "./Book1.xlsx";
const workbook = XLSX.readFile(filename);
const worksheet = workbook.Sheets["Calendar Config"]; 
const rowMajor = XLSX.utils.sheet_to_json(worksheet, {header: 1});

// Performing transpose from row-major to column-major
// Source: https://github.com/SheetJS/sheetjs/issues/1729
const rawTransposed = [];
for (let i=0; i<rowMajor.length; i++) {
  for (let j=0; j<rowMajor[i].length; j++) {
    if (!rawTransposed[j]) rawTransposed[j] = [];
    rawTransposed[j][i] = rowMajor[i][j]; 
  }
}

// Removing undefined values
const transposed = rawTransposed.map(arr => arr.filter(k => !!k));

console.log(transposed);

The resultant array will look like this:

[
  [ 'Calendar Name', 'Standard' ],
  [ 'Valid From', 44197 ],        
  [ 'Valid To', 44561 ],
  [ 'Use Holidays', 'yes' ],      
  [
    'Working Day',
    'Monday',
    'Tuesday',
    'Wednesday',
    'Thursday',
    'Friday',
    'Saturday',
    'Sunday'
  ],
  [
    'Start',
    0.3333333333333333,
    0.3333333333333333,
    0.3333333333333333,
    0.3333333333333333,
    0.3333333333333333,
    '-',
    '-'
  ],
  [
    'End',
    0.8333333333333334,
    0.8333333333333334,
    0.8333333333333334,
    0.8333333333333334,
    0.8333333333333334,
    '-',
    '-'
  ]
]

With this structure, transforming the array into the desired object becomes more straightforward, such as converting 'yes' to 'true', processing dates and blending working days information.

const XLSX = require("xlsx");
const filename = "./Book1.xlsx";
const workbook = XLSX.readFile(filename);
const worksheet = workbook.Sheets["Calendar Config"]; 
const rowMajor = XLSX.utils.sheet_to_json(worksheet, {header: 1});

// Transposing from row-major to column-major
// Reference: https://github.com/SheetJS/sheetjs/issues/1729
const rawTransposed = [];
for (let i=0; i<rowMajor.length; i++) {
  for (let j=0; j<rowMajor[i].length; j++) {
    if (!rawTransposed[j]) rawTransposed[j] = [];
    rawTransposed[j][i] = rowMajor[i][j]; 
  }
}

// Removing undefined values
const transposed = rawTransposed.map(arr => arr.filter(k => !!k));

// console.log(transposed);

// Function to convert Excel date serial number to date using JavaScript
function xlDateConvert(xlIndex) {
  const d = new Date(Math.round(xlIndex - 25569) * 86400000);
  return d.toISOString();
}

// Function to convert Excel time serial number to time format with UTC offset adjustment
function xlTimeConvert(xlIndex, utcOffset) {
  const hours = Math.floor((xlIndex % 1) * 24);
  const minutes = Math.floor((((xlIndex % 1) * 24) - hours) * 60)
  const d = new Date(Date.UTC(0, 0, xlIndex, hours - utcOffset, minutes));
  return d.toLocaleTimeString("en-IT", {hour: "2-digit", minute:"2-digit", hour12: false});
}

// Creating custom object
const index = Array.from({length: 5}, (k, i) => i); // Representing 5 keys in the object
const output = index.reduce((acc, curr, idx) => {
  switch (curr) {
    case 0: // name 
      acc["name"] = transposed[idx].slice(1)[0];
      break;
    case 1: // validFrom
      acc["validFrom"] = xlDateConvert(transposed[idx][1]);
      break;
    case 2: // validTo
      acc["validTo"] = xlDateConvert(transposed[idx][1]);
      break;
    case 3: // useHolidays
      acc["useHolidays"] = transposed[idx][1] === "yes" ? true : false;
      break;
   case 4: // workingDays
     acc["workingDays"] = transposed[idx].slice(1).map((arr, i) => {
       const start = transposed[idx + 1][i + 1];
       const end = transposed[idx + 2][i + 1];
       const dayStart = start === "-" ? start : xlTimeConvert(start, 10);
       const dayEnd = end === "-" ? end : xlTimeConvert(end, 10);
       return {
         dayIndex: i,
         dayStart: dayStart,
         dayEnd: dayEnd
       }
     })
   default:
     break;
  }
  return acc;
}, {});

// Adding a custom property
output["uploadedBy"] = "foo";

// Outputting the result
console.log(output);

This operation yields the following result:

{
  name: 'Standard',
  validFrom: '2021-01-01T00:00:00.000Z',
  validTo: '2021-12-31T00:00:00.000Z',
  useHolidays: true,
  workingDays: [
    { dayIndex: 0, dayStart: '08:00', dayEnd: '20:00' },
    { dayIndex: 1, dayStart: '08:00', dayEnd: '20:00' },
    { dayIndex: 2, dayStart: '08:00', dayEnd: '20:00' },
    { dayIndex: 3, dayStart: '08:00', dayEnd: '20:00' },
    { dayIndex: 4, dayStart: '08:00', dayEnd: '20:00' },
    { dayIndex: 5, dayStart: '-', dayEnd: '-' },
    { dayIndex: 6, dayStart: '-', dayEnd: '-' }
  ],
  uploadedBy: 'foo'
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Using boolean flags in JavaScript and jQuery

I'm having trouble setting a boolean flag in JavaScript/jQuery. I thought that the flags should change globally after clicking btn1, but for some reason they remain unchanged when clicking btn2. What am I missing? JavaScript: $(document).ready(funct ...

``There are problems with parsing JSON data due to the error message indicating the presence of unexpected

I am encountering an issue with displaying values from objects stored as string[] in appwriteDB. When trying to use *ngFor to iterate through the data, I faced difficulties. Despite attempting to convert the orderItems using JSON.parse(), the process faile ...

Issue with JQuery UI Tabs not displaying additional HTML pages in JavaScript

One issue I'm facing is that I added Tabs from JQuery UI on my website. The tab containing paragraphs loads fine, but the tabs linked to other HTML pages (which don't have tabs) won't load. Here is my .js file code and .html code: $(funct ...

What is the best way to eliminate the ' from every element in an array in this situation?

My form has hidden fields with values enclosed in single quotes like 'enable'. Using jQuery, I extract these values and push them into an array. This array is then sent to a PHP file using JavaScript through the url attribute of the jQuery AJAX m ...

Guide on setting a value in $(document).ready using code behind

I am currently working on a .NET application with the use of Twitter Bootstrap. My main challenge right now is retrieving data from a .aspx.cs page to a .aspx page. Here's a look at my code: strObject.cs public class strObject { public string Nam ...

The inclusion of <script> tag within the response received from an Ajax

Is there a way to update an element's innerHTML using Ajax.request that includes <script> tags? <div><script type="text/javascript">javascript</script></div> I want to update the content of a div with code from above wi ...

Utilizing $.getJSON to initiate a selection change event

I'm currently working on implementing a feature that involves adding categories to a dropdown list using jQuery Ajax. The goal is to load subcategories when a particular option is selected. However, I've encountered an issue where the addition o ...

Discovering the method for accessing a variable within jQuery from a regular JavaScript function

As someone new to jQuery, I am currently facing a challenge with accessing a variable defined inside a jQuery block from a regular function. Despite my attempts, I have been unsuccessful in accessing it. Can anyone guide me on how to do this? <script l ...

Sharing information from Directive to Component in Angular

Here is a special directive that detects key strokes on any component: import { Directive, HostListener } from '@angular/core'; @Directive({ selector: '[keyCatcher]' }) export class keyCatcher { @HostListener('document:keydo ...

Updating a singular value in an array using jQuery/JavaScript

Within a Javascript function, I have created an array called HM_Array1. The contents of the array are listed below: HM_Array1 = [[,11,147,,,,,,,1,1,0,0,0,1,"csiSetBorder(this)","null",,,true,["&nbsp;&nbsp;&nbsp;Accoun&nbsp;&nbsp;& ...

Steps for choosing the nth HTML row with jQuery

I'm facing a situation where I need to be able to select the nth row of an HTML table based solely on the id of the selected row. You can see exactly what I mean by checking out this JSFiddle Demo <table class="mytable1"> <tr><td i ...

Activate the field once the input for the other field is completed

I have a form where the last name field is initially disabled. How can I make it so that the last name field becomes enabled only when the first name is inputted? <form> <label for="fname">First name:</label><br> ...

Creating dynamic dxi-column with different data types in dxDataGrid

Our team is currently working on an angular application that involves displaying records in a dxdatagrid. The challenge we are facing includes: Different schema each time, with data coming from various tables. The need to add/edit records. Displayi ...

Enhancing JavaScript functions with type definitions

I have successfully implemented this TypeScript code: import ytdl from 'react-native-ytdl'; type DirectLink = { url: string; headers: any[]; }; type VideoFormat = { itag: number; url: string; width: number; height: number; }; type ...

Incorporating an NPM module with dependencies within the Meteor framework

I'm encountering some difficulties while attempting to integrate an NPM package into my meteor project. The specific module I am trying to utilize is the steam package. In order to make this work, I have included the meteorhacks:npm package for mete ...

Steps for aligning the upper rectangular text in the center of the larger rectangular border

https://i.stack.imgur.com/7yr5V.png I was aware of a particular element in html that had text positioned in the upper left corner, but my knowledge didn't go beyond that. Should I be adjusting the translation on both the X and Y axes based on the par ...

Experiencing issues with passwords in nodemailer and node

Currently, I am utilizing nodemailer in conjunction with Gmail and facing a dilemma regarding the inclusion of my password. The predicament stems from the fact that my password contains both single and double quotes, for example: my"annoying'password. ...

Generate HTML elements within an AngularJS directive and establish a click event

Can you show me how to create some DOM elements and add a click event to them using an AngularJS directive? My current approach is as follows: var list = document.createElement("div"); list.classList.add('myList'); for(var i = 0; i < n; i++) ...

Retrieving data from a subcollection in a cloud firestore database does not yield any results

In my Next.js application, I am utilizing Cloud Firestore database to store user documents. The structure of the collection path is as follows: collection "userDocs" └─ document "userEmail" └─ collection "docs" └─ document "document ...

Utilizing Selenium to traverse through nested loops and extract data from multiple tables

I am currently working on a project that involves extracting data from multiple tables on a website. So far, I have managed to extract data from one table successfully. However, I am struggling with creating a loop to retrieve data from all tables with the ...