JavaScript-Based Header Features Similar to Excel

Currently, I am in the process of developing a function that generates a sequence of strings similar to Excel headers. For those who may not be familiar with Excel, the sequence goes like this:

A,B,...,Z,AA,...,AZ,BA,...,ZZ,AAA,...,etc.

Here is the code snippet I have drafted so far:

function next(id) {
    if(id === "")
        return "A";
    var prefix = id.substring(0, id.length-1);
    var last = id[id.length-1]
    if(last === "Z")
        return (next(prefix) + "A");
    return prefix + String.fromCharCode(id.charCodeAt(id.length-1) + 1);
}

Are you aware of any alternative or more efficient methods for accomplishing this task?

Answer №1

If you're struggling with this problem, don't worry! I have created a simple solution that is easy to understand and comes with built-in tests.

All you have to do is call the function "toExcelHeaderString(4)" for columns A, B, C, and D.

Alternatively, if you need to generate headers for individual excel rows, use "toExcelHeader(4)" for column D.

/**
 * @param {Number} rows
 * @returns {String}
 */
toExcelHeaderString = function (rows) {
    return toExcelHeaderArray(rows).join(",");
}

// toExcelHeaderString(60) == "A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,AA,AB,AC,AD,AE,AF,AG,AH,AI,AJ,AK,AL,AM,AN,AO,AP,AQ,AR,AS,AT,AU,AV,AW,AX,AY,AZ,BA,BB,BC,BD,BE,BF,BG,BH"

/**
 * @param {Number} rows
 * @returns {Array}
 */
toExcelHeaderArray = function (rows) {
    var excelHeaderArr = [];
    for(var index = 1; index <= rows; index++) {
        excelHeaderArr.push(toExcelHeader(index));
    }
    return excelHeaderArr;
}

toExcelHeaderArray(60) == ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "AA", "AB", "AC", "AD", "AE", "AF", "AG", "AH", "AI", "AJ", "AK", "AL","AM", "AN", "AO", "AP", "AQ", "AR", "AS", "AT", "AU", "AV", "AW", "AX", "AY", "AZ", "BA", "BB", "BC", "BD", "BE", "BF", "BG", "BH"]

/**
 * @param {Number} index
 * @returns {String}
 */

toExcelHeader = function (index) {
    if(index <= 0) {
        throw new Error("index must be 1 or greater");
    }
    index--;
    var charCodeOfA = ("a").charCodeAt(0); // you could hard code to 97
    var charCodeOfZ = ("z").charCodeAt(0); // you could hard code to 122
    var excelStr = "";
    var base24Str = (index).toString(charCodeOfZ - charCodeOfA + 1);
    for(var base24StrIndex = 0; base24StrIndex < base24Str.length; base24StrIndex++) {
        var base24Char = base24Str[base24StrIndex];
        var alphabetIndex = (base24Char * 1 == base24Char) ? base24Char : (base24Char.charCodeAt(0) - charCodeOfA + 10);
        // bizarre thing, A==1 in first digit, A==0 in other digits
        if(base24StrIndex == 0) {
            alphabetIndex -= 1;
        }
        excelStr += String.fromCharCode(charCodeOfA*1 + alphabetIndex*1);
    }
    return excelStr.toUpperCase();
}
// toExcelHeader(0) == Error
// toExcelHeader(1) == "A"
// toExcelHeader(26) == "Z"
// toExcelHeader(27) == "AA"
// toExcelHeader(64) == "CB"
// toExcelHeader(12345) == "PAH"
// toExcelHeader(456789) == "YPFU"

Answer №2

It has been noted that @aqm's answer may produce an incorrect result when the index exceeds 702.

Upon counting from A to Z and AA to ZZ, it is evident that the total count reaches 702 (26 + 26 * 26 = 702). Hence, for toExcelHeader(703), the expected output should be 'AAA', but unfortunately, the function currently returns 'ABA'.

Fortunately, a revised version is provided below:

(Unfortunately, I do not have enough reputation to respond within the original thread)

function toExcelHeader(num) {

  if(num <= 0) {
    return '';
  }

  var str = num.toString(26);
  var arr = str.split('').map(char => {
    var code = char.charCodeAt(0);
    if(code >= 48 && code <= 57) {
      code += 16; // convert 1-9 to A-I and 0 to @
    } else {
      code -= 23; // convert a-p to J-Z
    }
    return code;
  });

  // convert 'A@' to 'Z', 'B@' to 'AZ', etc. 
  // ascii code of '@' is 64
  var index = arr.indexOf(64)
  while(index >= 0) {
    if(index == 0) {
      arr.shift();  // remove head '@'
    } else {
      arr[index] += 26;
      arr[index - 1]--;
    }
    index = arr.indexOf(64);
  }

  var chars = arr.map(code => String.fromCharCode(code));
  return chars.join('');
}

// toExcelHeader(0) == ""
// toExcelHeader(1) == "A"
// toExcelHeader(26) == "Z"
// toExcelHeader(27) == "AA"
// toExcelHeader(702) == "ZZ"
// toExcelHeader(703) == "AAA"
// toExcelHeader(18278) == "ZZZ"
// toExcelHeader(18279) == "AAAA"

Answer №3

Discovered this simple function in a package I found at this link

 const letters =
      ['A', 'B', 'C', 'D', 'E',
        'F', 'G', 'H', 'I', 'J',
        'K', 'L', 'M', 'N', 'O',
        'P', 'Q', 'R', 'S', 'T',
        'U', 'V', 'W', 'X', 'Y', 'Z'];

    const convertToExcelHeader = (index) => {
      index -= 1;

      const quotient = Math.floor(index / 26);
      if (quotient > 0) {
        return convertToExcelHeader(quotient) + letters[index % 26];
      }

      return letters[index % 26];
    };

convertToExcelHeader(0) === undefined            
convertToExcelHeader(1) === "A"           
convertToExcelHeader(26) === "Z"          
convertToExcelHeader(27) === "AA"         
convertToExcelHeader(702) === "ZZ"        
convertToExcelHeader(703) === "AAA" 
convertToExcelHeader(2074) === "CAT"
convertToExcelHeader(3101) === "DOG"      
convertToExcelHeader(18278) === "ZZZ"     
convertToExcelHeader(18279) === "AAAA"

Answer №4

function generateColumnNames(number) {
  let output = [];

  const startCharCode = "A".charCodeAt(0);
  const endCharCode = "Z".charCodeAt(0);

  let alphabetSize = endCharCode - startCharCode + 1;
  const repeatCount = Math.floor(number / alphabetSize);

  let currentIndex = 0;
  let initialString = "";
  let charValue = "";

  while (currentIndex <= repeatCount) {
    if (currentIndex > 0) {
      initialString = String.fromCharCode(startCharCode + currentIndex - 1);
    }

    if (currentIndex === repeatCount) {
        alphabetSize = number % alphabetSize;
      }

    for (var i = 0; i < alphabetSize; i++) {
      charValue = String.fromCharCode(startCharCode + i);

      output.push(initialString + charValue);
    }
    currentIndex++;
  }

  console.log(output, output.length);
  return output;
}

generateColumnNames(55);

Answer №5

Although this thread may be old, I couldn't find any tips when searching for some guidance. So, I decided to come up with my own solution and share it here in case it can benefit others. This code creates headers like: A, B.. Z, AA-AZ, AB-AZ, ... ZZ-ZZ, allowing a maximum of 702 headers. If you need more, you can customize the function accordingly.

The function takes a number representing the amount of Excel-like headers to generate and requires an array of ALPHABETICAL_ORDERED_LETTERS ('abcdefghijklmnopqrstuvwxyz'.toUpperCase().split('')).

For example: buildExcelLikeHeaders(59)


const MAX_LENGTH = 702;
// capable of handling up to 702 columns;

function buildArray(remainingRows, prevArrOfHeaders = [], size, letter = 'A', index = 0) {
  const newSetOfHeaders = [];
  let currentIndex = index;

  if (currentIndex > MAX_LENGTH) currentIndex = MAX_LENGTH;

  for (let i = 0; i < remainingRows; i++) {
    if (ALPHABETICAL_ORDERED_LETTERS[i]) {
      const header = `${letter + ALPHABETICAL_ORDERED_LETTERS[i]}-${letter}Z`;
      newSetOfHeaders.push(header);
    }
  }

  const nextArrayOfHeaders = [...prevArrOfHeaders, ...newSetOfHeaders];

  const remainingRowsNext = Math.abs(remainingRows - ALPHABETICAL_ORDERED_LETTERS.length);
  
  if (nextArrayOfHeaders.length < size && nextArrayOfHeaders.length < MAX_LENGTH) {
    currentIndex++;

    return buildArray(
      remainingRowsNext,
      nextArrayOfHeaders,
      size,
      ALPHABETICAL_ORDERED_LETTERS[currentIndex].toUpperCase(),
      currentIndex,
    );
  }

  return nextArrayOfHeaders;
}

const buildExcelLikeHeaders = row => {
  if (row <= ALPHABETICAL_ORDERED_LETTERS.length) {
    return ALPHABETICAL_ORDERED_LETTERS.slice(0, row);
  }

  const remainingRows = row - ALPHABETICAL_ORDERED_LETTERS.length;
  const referenceSize = row;

  return buildArray(remainingRows, ALPHABETICAL_ORDERED_LETTERS, referenceSize);
};

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

A collection of collections

Alright, listen up. I've got a JSON file with an array inside another array. Here's a snippet of the JSON file: { "keys": [ { "game": "Counter-Strike: Global Offensive", "price": "5", "listofkeys" ...

Preserve the XHR-generated configuration when the browser is refreshed

My PHP page features a navigation menu with options such as Home, About Us, and Profile. When clicking on the "About Us" link from the home page, it loads an Ajax response displaying the information without reloading the page. However, if the user decide ...

Tips for personalizing the Material UI autocomplete drop-down menu

I'm currently working with Material UI v5 beta1 and I've been attempting to customize the Autocomplete component. My goal is to change the Typography color on the options from black to white when an item is selected. However, I'm struggling ...

What is the process for displaying a PHP array in HTML5 audio and video players?

I am currently working with two PHP arrays. The first array, array "a," contains strings that represent paths for MP3 files on the server. The second array, array "b," contains strings representing paths for MP4 files. For example: $test = 'a.mp3&ap ...

Is it possible for a lambda in TypeScript to have access to the class scope but return undefined

In my TypeScript code, I have a class used as a Pipe in Angular 2 to render markdown. It compiles successfully but encounters a runtime exception on a specific line: var Remarkable = require('remarkable'); @Pipe({ name: 'markdown' ...

Flask and the steps to modify CORS header

While working on my localhost, I came across a CORS error when building an application to handle search queries for a different domain. The specific error was: "Cross Origin Request Blocked... (Reason: CORS header 'Access-Control-Allow-Origin' mi ...

Storing the information filled out in the form and utilizing it to navigate to the correct destination URL

With the generous assistance and insightful advice from members of Stack Overflow, I am nearing completion of my quiz project. However, I do have a few lingering questions regarding some final touches needed for the project. Before delving into those quest ...

Server-side script for communicating with client-side JavaScript applications

Currently utilizing a JavaScript library that uses a JSON file to display content on the screen in an interactive manner. (::Using D3JS Library) While working with clients, it is easy to delete, edit, and create nodes which are then updated in the JSON fi ...

What is the process for fetching the chosen outcome from a subsequent webpage using HTML?

How can I display selected cities on a different webpage after clicking a button? Currently, I can only get the results on the same page. For example, if a user selects NYC and Delhi, those cities should be displayed on another HTML page. ...

Locating the Active Object's Coordinates in fabric.js

When using fabric js, I have successfully drawn various shapes like circles and rectangles. However, I encountered an issue while trying to obtain the coordinates of the rectangle using the fabric.rect() method. canvas.getActiveObject().get('points& ...

javascript authorization for iframes

I am currently working on a localhost webpage (parent) that includes an iframe displaying content from another url (child; part of a different webapp also on localhost). My goal is to use JavaScript on the parent-page to inspect the contents of the iframe ...

The concept of using the `map` method within a

Hi there, I could use some assistance with a tricky issue I'm facing. My current task involves rendering a cart object that includes product names, prices, and quantities. Each product can have its own set of product options stored as an array of ob ...

Executing Statements in a Specific Order with Express and Sqlite3

I am having an issue creating a table and inserting an item into it using the node command. Despite my efforts to reorganize my script, the item is being inserted before the table is created. Interestingly, manually inputting the commands in sqlite3 works ...

Ways to restrict a function to a single DOM element using either its id or class

This script is responsible for dynamically loading pages on my website. It works fine, except that it currently iterates over all anchor tags a on the site, whereas I want it to iterate only through my navigation menu. Here is the navigation menu: <div ...

Implementing a change event upon setting a value to an input element using JavaScript

My plan is to develop a Chrome extension that can automatically fill passwords. The code for this functionality looks like the following: //get the account document.querySelector("input[type=text]").addEventListener('input', () => { ...

Regex pattern is malfunctioning

I am having trouble understanding why this regex keeps returning false. I have an onkeydown event that should trigger it when pressing the 'w' key, but it doesn't seem to be working. var keyGLOB = ''; function editProductSearch ( ...

Tips for including parameters in an array of values when using getStaticPaths

I'm trying to retrieve the slug value that corresponds with each number in the getStaticPaths for my page structure: /read/[slug]/[number]. The code I have is as follows: export async function getStaticPaths() { const slugs = await client.fetch( ...

Resolving the Table Issue with 'onclick' in Javascript

Apologies for the lack of creativity in the title, I struggled to come up with something fitting. Currently, I am engaged in the development of a user-friendly WYSIWYG site builder. However, I have encountered an obstacle along the way. I've devised ...

Using Axios to Integrate an API - Displaying a Username and Password Pop-up

I have been given a project that involves API integration. I have successfully retrieved data from the API, but there is a pop-up appearing asking for a username and password. Although I have these credentials, I would like to log in without that pop-up ap ...

When filtering an array in JavaScript, ensure to display a notification if no items match the criteria

In React/Next, I have an array that is being filtered and sorted before mapping through it. If nothing is found after the filters run, I want to display a simple message in JSX format. The message should be contained within a span element. Here is the str ...