Efficient method to access two arrays simultaneously and combine them into an associative array in JavaScript

When using Ajax to return a table, you have the option of separating column names and row values. Here are two ways to do it:

let columns = ["col1", "col2", "col3"];
let rows = [
      ["row 1 col 1", "row 1 col 2", "row 1 col 3"]
    , ["row 2 col 1", "row 2 col 2", "row 2 col 3"]
    , ["row 3 col 1", "row 3 col 2", "row 3 col 3"]
    , ["row 4 col 1", "row 4 col 2", "row 4 col 3"]
    , ["row 5 col 1", "row 5 col 2", "row 5 col 3"]
];

You can also use an associative array like this:

let rows = [
      { "col1": "row 1 col 1", "col2": "row 1 col 2", "col3": "row 1 col 3" }
    , { "col1": "row 2 col 1", "col2": "row 2 col 2", "col3": "row 2 col 3" }
    , { "col1": "row 3 col 1", "col2": "row 3 col 2", "col3": "row 3 col 3" }
    , { "col1": "row 4 col 1", "col2": "row 4 col 2", "col3": "row 4 col 3" }
    , { "col1": "row 5 col 1", "col2": "row 5 col 2", "col3": "row 5 col 3" }
];

The first method results in less data transmission for larger amounts of data. However, accessing elements like rows[i]["col1] is not as straightforward compared to the second method.

To work around this issue, you can convert the rows and columns into an associative array like this:

let columns = ["col1", "col2", "col3"];

let data = [
      ["row 1 col 1", "row 1 col 2", "row 1 col 3"]
    , ["row 2 col 1", "row 2 col 2", "row 2 col 3"]
    , ["row 3 col 1", "row 3 col 2", "row 3 col 3"]
    , ["row 4 col 1", "row 4 col 2", "row 4 col 3"]
    , ["row 5 col 1", "row 5 col 2", "row 5 col 3"]
];

let arr = [];


for (let j = 0; j < data.length; ++j)
{
    let obj = {}; // Object

    for (let i = 0; i < columns.length; ++i)
    {
        obj[columns[i]] = data[j][i];
    }
    arr.push(obj);
}

If you're looking for a more efficient way to handle this in JavaScript without consuming too much time or memory, consider exploring alternative solutions such as proxies or indexed properties. These approaches may help streamline your code and improve performance.

Answer №1

Here are two different methods that can be utilized based on your data accessibility requirements.

If you prefer accessing the data "row-per-row," combining both approaches into a singular result would be more efficient. It doesn't seem necessary to have the column definitions in a separate array in this scenario.

let rows = [
      ["col1", "col2", "col3"],
      ["row 1 col 1", "row 1 col 2", "row 1 col 3"],
      ["row 2 col 1", "row 2 col 2", "row 2 col 3"],
      ["row 3 col 1", "row 3 col 2", "row 3 col 3"],
      ["row 4 col 1", "row 4 col 2", "row 4 col 3"],
      ["row 5 col 1", "row 5 col 2", "row 5 col 3"]
];

This method groups all entries of a row together, making it simple to handle empty cells with null or undefined values.

Alternatively, if you prefer accessing the data "column-per-column," using an object with the column header as key is recommended.

let table = {
    "col1": ["row 1 col 1", "row 2 col 1", ..."],
    "col2": ["row 1 col 2", "row 2 col 2", ..."]
}

This approach also allows for quick and easy retrieval of data for a specific row entry using table['col1'][index].

Answer №2

After figuring out the solution myself.
It is possible to create a class that shares the current row index between two proxies:

interface IProxyHandler
{
    get(obj, prop, receiver);
}


declare class Proxy
{
    public constructor(obj, handler_callback: IProxyHandler);
}

interface SomeTable
{
    col1: string;
    col2: number;
    col3: Date;
}

export class table<T>
{

    public obj: T[];
    //public columns: map<string, number>;
    public columns: { [key: string]: number };

    protected i: number;
    protected accessor: Proxy;


    //get bar(): boolean
    //{
    //    return null; // this._bar;
    //}
    //set bar(theBar: boolean)
    //{
    //    //this._bar = theBar;
    //}

    public row(index:number): T
    {
        this.i = index;
        return <T><any>this.accessor;
    }

    public rows :T[];

    constructor(rows: any[][], columnNames:string[])
    {
        this.obj = <any>rows;
        this.i = 0;

        // this.columns = columnNames;
        this.columns = {}; // "associative array" or Object

        for (let i = 0; i < columnNames.length; ++i)
        {
            this.columns[columnNames[i]] = i;
        }


        let handler: IProxyHandler = {
            get: function (obj, prop, receiver)
            {
                return this.obj[this.i][this.columns[prop]];
            }
        };

        handler.get = handler.get.bind(this);
        this.row = this.row.bind(this);
        this.accessor = new Proxy(this.obj, handler);

        let handler2: IProxyHandler = {
            get: function (obj, prop, receiver)
            {
                return this.row(prop);
            }
        };
        handler2.get = handler2.get.bind(this);

        this.rows = <any> new Proxy(this.obj, handler2);
    }


}


// https://caniuse.com/#feat=proxy
// Sorry, your browser is no longer supported. 
// If you want this program to support IE11, develop a proxy-polyfill for IE11. 
// Hint from Babel-docs: ES2015-Proxies requires support on the engine level; 
// it is thus not possible to polyfill Proxy in ES5.
export function testTable()
{
    let columns = ["col1", "col2", "col3"];
    let rows = [
        ["row 1 col 1", "row 1 col 2", "row 1 col 3"]
        , ["row 2 col 1", "row 2 col 2", "row 2 col 3"]
        , ["row 3 col 1", "row 3 col 2", "row 3 col 3"]
        , ["row 4 col 1", "row 4 col 2", "row 4 col 3"]
        , ["row 5 col 1", "row 5 col 2", "row 5 col 3"]
    ];

    let x = new table<SomeTable>(rows, columns);

    console.log(x.rows[0].col1);
    // console.log(x.row(1).col1);
    // console.log(x.obj[0][0]);
}

PS:
If I were to use a function, I'd need to do this:

public rowz(index: number, colName: string): any
{
    this.i = index;
    return this.obj[index][this.columns[colName]];
}

or this

public rowz(index: number): any
{
    this.i = index;

    return function (colName)
    {
        this.obj[index][this.columns[colName]];
    };

}

This has the disadvanages that I would lose compile-time type-safety, both with the columnName as well as with the return type. Or I would need to copy the entiry row into an object, which duplicates data/memory-consumption.


Another option, which is compatible with ES5/IE11, is using object-properties:

interface ITestTable1
{
    col1:number;
    col2:number;
}


interface ITestTable2
{
    a:number;
    b:number;
    c:number;
}



export class TableWrapper<T>
{
    public rows:any[][];
    protected m_accessor:object;
    protected m_i:number;

    protected m_columnMap: { [columnName: string]: number; };
    protected m_columns: string[];
    protected m_columnLength:number;


    public get rowCount(): number
    {
        return this.rows.length;
    }


    public get columnCount(): number
    {
        return this.m_columns.length;
    }


    get columns(): string[]
    {
        return this.m_columns;
    }


    protected setColumns(cols: string[])
    {
        this.m_columnLength = cols.length;

        this.m_columnMap = null;
        this.m_columnMap = {};

        for (let i = 0; i < this.m_columnLength; ++i)
        {
            this.m_columnMap[cols[i]] = i;
        }

        this.m_columns = cols;
    } // End Sub setColumns 


    public row(i:number):T
    {
        this.m_i = i;
        return <T><any>this.m_accessor;
    }


    public getIndex(name:string):number
    {
        return this.m_columnMap[name];
    }


    public addRow(dat:any[])
    {
        this.rows.push(dat);
        return this;
    }


    public removeRow(i:number)
    {
        this.rows.splice(i, 1);
        return this;
    }


    constructor(columns: string[], data: Array<any[]>, ignoreCase?:boolean)
    {
        if (ignoreCase == null)
            ignoreCase = true;

        for (let i = 0; i< columns.length; ++i)
        {
            columns[i] = columns[i].toLowerCase();
        } // Next i 


        let that = this;
        this.getIndex.bind(this);
        this.setColumns.bind(this);
        this.row.bind(this);
        this.addRow.bind(this);
        this.removeRow.bind(this);

        this.rows = data;
        this.setColumns(columns);
        this.m_accessor = { }; // Creates a new object


        for (let i = 0; i < columns.length; ++i)
        {
            let propName = columns[i];

            Object.defineProperty(this.m_accessor, propName, {
                // Using shorthand method names (ES2015 feature). 
                // get() { return bValue;}, 
                // set(value) { bValue = value;}, 
                // This is equivalent to: 
                // get: function() { return bValue; }, 
                // set: function(value) { bValue = value; }, 
                // And could be written as (getter = getter.bind(this)) 
                // get: getter, 
                // set: setter, 
                get: function ()
                { 
                    let currentRow =  <any> that.rows[that.m_i];
                    return currentRow == null ? currentRow : currentRow[i]; 
                },
                set: function(value:any) 
                { 
                    let currentRow =  <any> that.rows[that.m_i];
                    if (currentRow!= null )
                        currentRow[i] = value; 
                },
                enumerable: true,
                configurable: true
            });
        } // Next i 

    }

}


let tab: TableWrapper<ITestTable1> = new TableWrapper<ITestTable1>(["col1","col2"], [[1,2], [3,4]]);
// tab.columns=["col1","col2", "col3"];

let hi :TableWrapper<ITestTable2>= new TableWrapper<ITestTable2>(["a","b","c"], [[1,2,3],[4,5,6] ]);


console.log(tab.row(0).col1);
console.log(hi.row(0).a);
console.log(hi.row(1).b);
console.log(hi.row(0).c);

hi.row(0).a = 123;


for (let i = 0; i< hi.rowCount; ++i)
{
    for (let j=0; j < hi.columnCount; ++j)
    {
        console.log(hi.rows[i][j]);

        console.log(hi.row(i).a);
        console.log(hi.row(i).b);
        console.log(hi.row(i).c);

        console.log((<any>hi.row(i))[hi.columns[j]]);
        console.log((<any>hi.row(i))[hi.columns[j]]);
        console.log((<any>hi.row(i))[hi.columns[j]]);
    }

}

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

Ways to eliminate submenu tooltips

Whenever I hover over a menu item with submenu pages in the wordpress backend, a "tooltip" pops up showing each submenu page. How can I remove these tooltips? I have attempted to remove the wp-has-submenu style class, which somewhat works. The tooltip no ...

Dynamic links with Node Acl

Utilizing the npm module Acl to establish an ACL system has been my current focus. You can check out the homepage of this module at: https://github.com/OptimalBits/node_acl. While the documentation provides straightforward examples for granting role acces ...

Using Javascript to dynamically add rows to a table, however they mysteriously appear and disappear soon

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="UserPermisson.aspx.cs" Inherits="TestProjects.UserPermisson" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" ...

Applying CSS rules from an array to elements by looping through

I'm looking for a way to allow users to input CSS styles and have those styles applied to the last selected element, which is determined by the "rangeselector" variable. Currently, the code selects the correct element, but only the first CSS rule is b ...

Error: The variable "details.date.getTime" is not defined and cannot be accessed

Currently, I am utilizing https://github.com/zo0r/react-native-push-notification to display notifications. Specifically, I am using scheduled notifications with datetimepicker. Previously, I have successfully used this in another project without any errors ...

Can you explain the significance of { 0: T } in this particular type definition?

I stumbled upon this type declaration in my codebase which is meant for non-empty arrays: type NonEmptyArray<T> = T[] & { 0: T } and it functions as expected: const okay: NonEmptyArray<number> = [1, 2]; const alsoOkay: NonEmptyArray<n ...

The JQuery function .load() is not functioning correctly

How can I dynamically load a webpage's content into a div using JavaScript and jQuery? Here's what I have tried: <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script> When a s ...

The oncanplaythrough event is not functioning properly in Internet Explorer

I am facing an issue where the beep sound trigger upon receiving an API response works perfectly in Chrome and Firefox browsers, but unfortunately, it does not work in Internet Explorer. if ($scope.totalQueueList) { var audio = new Audio(); audio.s ...

Achieve top-notch performance with an integrated iFrame feature in Angular

I am trying to find a method to determine if my iframe is causing a bottleneck and switch to a different source if necessary. Is it possible to achieve this using the Performance API? This is what I currently have in my (Angular) Frontend: <app-player ...

Collaboratively utilizing resources among various NPM Workspaces

Currently, I am working on a React project using NPM Workspaces. I have created an 'assets' workspace within this project to store all images and assets that need to be accessed by other workspaces. The directory structure of the project is as fo ...

Please proceed with submitting your choices in the order that you have selected

My goal is to submit options from a dropdown list in the order they are selected, rather than in the order they appear in the list. How can I achieve this? Here is the HTML code for the dropdown: < select style = "padding: 1em;" name = "skills" multi ...

Is there a way to retrieve random data based on either product id or slug within a single component using useQuery? Currently, all components are displaying the same data

Here is the code I wrote: const fetchCartItem = async () => { if (token) {const {data } = await axios.get(API.GET_PRODUCT_DETAILS_BY_PRODUCT_ID.replace("[ProductID]",item?.ProductID),{headers:{Authorization: token,},});setCartLoading(fal ...

Determining the orientation of an image in JavaScript

Currently, I am attempting to determine the orientation of images using JavaScript in order to apply a specific class to them. This process seems to be functioning correctly on Firefox, but is presenting challenges on other browsers. It appears to work bet ...

Challenges Encountered when Making Multiple API Requests

I've encountered a puzzling issue with an ngrx effect I developed to fetch data from multiple API calls. Strangely, while some calls return data successfully, others are returning null for no apparent reason. Effect: @Effect() loadMoveList$: Obse ...

Effectively monitoring coordinates on both the client and server sides

I'm currently in the process of developing a multiplayer game using websockets, node, and JavaScript. I need advice on the most effective approach to update client information and manage their coordinates on the server. The method I am using at the mo ...

The state remains unchanged when useState is invoked

I am currently designing a unique restaurant website template. As part of the project, I have implemented a modal box with two arrow buttons to navigate between pages. The issue I am facing is that the value of the pageNumber variable, tracked using useSta ...

Tips for reloading a page when using the back button on MAC Safari

On my ASP.NET MVC application, I have two pages (A and B) with checkboxes and textboxes on page A. After moving from page B to page A using the browser back button in Safari on MAC, the page does not refresh and retains the old values for checkboxes and t ...

Set up an array data by extracting values from an array prop within a Vue component

Within my Vue component, I am dealing with an array prop called selectedSuppliers that consists of objects. My goal is to set up a data property named suppliers and initialize it with the values from selectedSuppliers. However, I do not want any modificati ...

Angular 5+ does not have any compatible call signatures for Type Search

Utilizing an addItem function that I found on this site, I encountered the following error message: Error TS2349 (TS) Cannot invoke an expression whose type lacks a call signature. Type 'Search' has no compatible call signatures. Definition o ...

Is it possible to conceal an HTML form once it has been submitted

I have a form on my website that sends the entered information to a PHP script which then emails me the details. I've implemented JavaScript form validation and jQuery Ajax to ensure the page doesn't need to be reloaded. Here is the HTML code fo ...