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

Potential keys + keys that are present in the `initialData`

Is there a way to specify the type of data in order to include all keys that exist in initialData plus additional keys from Item as Partial(optional)? class TrackedInstance<Item extends Record<string, any>, InitialData extends Partial<Item> ...

Make sure to close any existing Featherlight windows before trying to open another one

I'm setting up multiple featherlight instances when the page loads jQuery('.feedback').featherlight(jQuery( "#feedback-box" ), { closeIcon: 'close'}); jQuery('#imprint').featherlight(jQuery( "#imprint-box" ), { closeIcon ...

The second occurrence of a jQuery event

When a user left-clicks on the menu, it should load a view in a draggable box. However, the functionality is not working as expected. Sometimes you need to click twice - the first time the box appears but is not draggable, and the second time a new box app ...

Is it possible to run two commands in npm scripts when the first command initiates a server?

When running npm scripts, I encountered an issue where the first command successfully starts a node server but prevents the execution of the second command. How can I ensure that both commands are executed successfully? package.json "scripts": { "dev ...

Problem with sorting when using $in operator in MongoDB

I am looking to retrieve documents from a MongoDB collection where the IDs match those in an array: [ '5f80a44d0179262f7c2e6a42', '5f8c00762fae890e9c4d029c', '5f802cf8abac1116a46bf9d4' ] The problem arises when the docu ...

Ways to access information from doc.data()

<template> <div> {{ id }} {{ title }} </div> </template> <script> import { useRoute } from 'vue-router' import 'firebase/firebase-firestore' import { db } from '@/fdb' export default ...

Looping through the json resulted in receiving a null value

When working with typescript/javascript, I encountered an issue while trying to fetch the 'statute' from a data object: {_id: "31ad2", x: 21.29, y: -157.81, law: "290-11",....} I attempted to assign data.law to a variable, but received a typeer ...

Implementing Scroll-Activated Class with Vanilla JavaScript

I'm currently developing a Vue.js project and I want to implement a feature where a "back to top" button appears when the user scrolls beyond a certain point. However, I'm encountering an issue without using JQuery. Can you help me troubleshoot t ...

IE does not support hover effects

Having trouble with a hover effect that works in Chrome but not in MSIE. Are there any alternatives or fixes to make it work in MSIE? The hover/rollover effects I've tried all seem to have this issue in IE. normalize.css demo.css set2.css font- ...

Best Practices for Implementing JSON.stringify() with an AJAX Request

While I have a limited understanding of ajax and JSON, I am aware that using JSON.stringify in an ajax call can sometimes be beneficial. The ajax call below is functioning properly, whereas the one following it with the stringify method is not. I am unsure ...

Receiving a response from a Node.js server using an AJAX GET request

Here's a snippet of code from app.js that retrieves an aggregated value from mongo.db. I'm attempting to use this response value on my HTML page by making an AJAX call. However, I'm facing an issue where I can't retrieve the value in my ...

Dart and external CSS and JS libraries and tools

As I prepare to dive into developing my very first web application, one technology that has caught my eye is Google Dart. The idea of using a new, innovative approach to web development excites me, and I am seriously considering utilizing it for my project ...

Issue with Prettier AutoFormatting in a project that combines TypeScript and JavaScript codebases

Recently, I've started incorporating TypeScript into an existing JavaScript project. The project is quite large, so I've decided to transition it to TypeScript gradually. Below is a snippet from my eslintrc.js file: module.exports = { parser: ...

What is the process of TypeScript module resolution within the Play framework?

In my Play project, I am interested in incorporating Angular 2 with TypeScript. Utilizing the sbt-typescript plugin and the angular2 WebJAR, I have encountered a situation where Play places the extracted WebJAR in target/web/public/main/lib/angular2. Ideal ...

What steps should I take to ensure my API secret remains secure during my next.js deployment?

Recently, I created a small internal application using React and Next.js. To keep my API key and secret secure, I followed the instructions on how to use an .env file provided by Next.js. In my api/hello.js file, I have a simple request that looks like th ...

DxDataGrid: Implementing a comprehensive validation system for multiple edit fields

I'm currently working with a DxDataGrid within an Angular Application. Within this particular application, I have the need to input four dates. I've implemented validation rules that work well for each individual field. However, my challenge aris ...

Utilizing PHP to iterate over a JSON array and generate HTML content

I am utilizing bootstrap datatables to present data retrieved from an API in a tabular format. Below is the sample data: { "response_status": { "status_code": 0, "status_message": [ { "reg_number": "123", "company": "Compan ...

What is the correct method for launching a modal window in wagtail-admin?

I am currently working on enhancing my wagtail-admin interface, but I have hit a roadblock when trying to open a modal window. While I could create a div with a close button, I believe there must be a more appropriate method for achieving this. It seems th ...

Creating a Draft.js selection with a specified start and end point

Looking for a way to replace the last insertion in Draft.js For instance, Original string -> aaazzz After inserting 'bbb' in the middle -> aaabbbzzz Replace last insert with 'ccc' -> aaaccczzz Replace last insert with &apos ...

Having difficulty retrieving the value from an input field, despite trying both text() and val() methods

I'm struggling to retrieve the value of an input field that a user enters and then use it in my code. I have attempted using both text() and val() to get the value from the input fields, but neither method seems to be working for me. If you have any ...