繁体   English   中英

如何以低开销在JavaScript中访问两个数组作为关联数组?

[英]How to access two arrays as associative array in JavaScript with low overhead?

通过Ajax返回表时,可以分别使用列名和行值来完成,如下所示:

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 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" }
];

现在,如果有大量数据,则第一种变体将导致较少的数据要传输。

问题是,我不能像rows [i] [“ col1]那样访问它,就像我使用第二个变体一样。

现在,我可以获取行和列,并从中创建一个关联数组,例如

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 = {}; // "associative array" or Object

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

但是然后我必须复制对象,如果rowCount和columnCount大,则可能要花费时间和内存。

是否有任何好的(非时间和非内存消耗)方式可以在JavaScript中实现呢?

我需要的是索引属性,但似乎它们在JavaScript中不存在...

我最接近的是代理对象:

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 cols = {}; // "associative array" or Object

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


let handler2 = {
    get: function (obj, prop, receiver)
    {
        return obj[cols[prop]];
    }
};

// https://www.sitepoint.com/es6-proxies/
let handler = {
    get: function (obj, prop, receiver)
    {
        console.log("obj:", obj, "prop:", prop, "receiver :", receiver);
        //return obj[prop];
        //return obj[cols[prop]];
        return new Proxy(obj[prop], handler2);
    }

    , set: function (obj, key, value)
    {
        console.log(obj, key, value);
    }

};

let p = new Proxy(rows, handler);

这里的问题是-除了IE11不支持代理-我还需要在每个行访问上创建一个新的代理,因为看起来代理不正确地支持对象数组...

是否有任何明智的方式以良好/高效的方式解决JavaScript中的特定问题?

关于我想要的最后一件事是编写这样的代码:

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 cols = {}; // "associative array" or Object

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

let index_col1 = cols["col1"];
let index_col2 = cols["col2"];
let index_col3 = cols["col3"];


for (var i = 0; i < rows.length; ++i)
{
    console.log("col1:", rows[i][index_col1], "col2:", rows[i][index_col2], "col3:", rows[i][index_col3]);
}

这是我目前要做的...

我可以建议两种不同的方法。 这完全取决于您以后实际需要如何访问数据。

如果您需要“逐行”,那么您的第一种方法还不错。 我只是将它们两者合并为一个结果。 我猜想将列定义放在单独的数组中是没有意义的。

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"]
];

在这种方法中,您将一行的所有条目分组在一起,甚至空单元格也很容易使用空值/未定义值进行处理。

如果您想访问“ column-per-column”数据,我建议使用一个以列标题为键的对象。

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

在这种方法中,也很容易(快速)获得行条目的数据。 表['col1'] [索引]

啊,我自己得到了答案。
通过创建一个类并在两个代理之间共享当前行索引,这是有可能的:

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:
如果要使用函数,则需要执行以下操作:

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

或这个

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

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

}

这样做的缺点是,无论使用columnName还是返回类型,我都会失去编译时的类型安全性。 或者,我需要将整个行复制到一个对象中,该对象将重复数据/内存消耗。


与ES5 / IE11兼容的另一个选项是使用对象属性:

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]]);
    }

}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM