【问题标题】:Dynamic modify or generate code in Javascript在 Javascript 中动态修改或生成代码
【发布时间】:2020-03-08 19:25:06
【问题描述】:

我正在对一些表格数据进行多重排序。我想动态修改 AngularJS 站点中指定排序优先顺序的代码行。 (我只是在修改数组的结构,因为 AngularJS 会观察绑定数据的变化并在 HTML 显示中动态地反映它们。)

对于多重排序,我使用thenBy.js。本质上,您创建一个以 firstBy(comparativeFunction) 开头的变量,然后您可以根据需要添加任意数量的 thenBy(comparativeFunction)

我会根据需要动态添加和删除排序比较器。这是我从这个简单的两级多排序开始的地方:

// I want to modify and append to this line
var s = firstBy( (a, b) => a["Status"].localeCompare(b["Status"]) )
  .thenBy( (a, b) => parseFloat(a["#"]) - parseFloat(b["#"]) );

它最初会根据“状态”属性中的数据对行进行排序,然后根据 ID 编号的“#”进行子排序。

我想知道的是如何在上面的行中动态添加更多代码。例如,假设我想添加一个三级排序,比如“唯一 ID”。我可以这样对行进行硬编码:

var s = firstBy( (a, b) => a["Status"].localeCompare(b["Status"]) )
  .thenBy( (a, b) => parseFloat(a["#"]) - parseFloat(b["#"]) )
  .thenBy( (a, b) => parseFloat(a["Unique ID"]) - parseFloat(b["Unique ID"]) );

我只是不确定如何以编程方式将更多 'thenBy()' 方法附加到这一行。我应该生成一个字符串并通过 eval() 运行它吗?有没有更好(更安全)的方法?

这是目前的节目:

self.dataSource = [
    {"#": "1", "Unique ID": "100130", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 1 - Box 1 - Position 1", "Status": "Available"},
    {"#": "2", "Unique ID": "100131", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 1 - Box 1 - Position 2", "Status": "Available"},
    {"#": "3", "Unique ID": "100132", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 1 - Box 1 - Position 3", "Status": "Available"},
    {"#": "4", "Unique ID": "100133", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 1 - Box 1 - Position 4", "Status": "Available"},
    {"#": "5", "Unique ID": "100134", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 1 - Box 1 - Position 5", "Status": "Checked Out"},
    {"#": "6", "Unique ID": "100135", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 1 - Box 1 - Position 6", "Status": "Checked Out"},
    {"#": "7", "Unique ID": "100136", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 1 - Box 1 - Position 7", "Status": "Checked Out"},
    {"#": "8", "Unique ID": "100137", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 1 - Box 1 - Position 8", "Status": "Checked Out"},
    {"#": "9", "Unique ID": "100138", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 1 - Box 1 - Position 1", "Status": "Available"},
    {"#": "10", "Unique ID": "100139", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 2 - Box 1 - Position 1", "Status": "Available"},
    {"#": "11", "Unique ID": "100140", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 2 - Box 1 - Position 2", "Status": "Available"},
    {"#": "12", "Unique ID": "100141", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 2 - Box 1 - Position 3", "Status": "Lost"},
    {"#": "13", "Unique ID": "100142", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 2 - Box 1 - Position 4", "Status": "Lost"},
    {"#": "14", "Unique ID": "100143", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 3 - Box 1 - Position 1", "Status": "Available"},
    {"#": "15", "Unique ID": "100144", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 3 - Box 1 - Position 2", "Status": "Available"},
    {"#": "16", "Unique ID": "100145", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 4 - Box 1 - Position 1", "Status": "Checked Out"},
    {"#": "17", "Unique ID": "100146", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 4 - Box 1 - Position 2", "Status": "Available"},
    {"#": "18", "Unique ID": "100147", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 4 - Box 1 - Position 3", "Status": "Available"},
    {"#": "19", "Unique ID": "100148", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 4 - Box 1 - Position 4", "Status": "Checked Out"},
    {"#": "20", "Unique ID": "100149", "Name": "Book", "Section": "Paraguay", "Position": "Shelf 1 - Rack 5 - Box 1 - Position 1", "Status": "Available"}       
];

var self = this;

self.multiSortDict = {};

self.addSort = function(sortName, sortComparatorFunction)
{
    self.multiSortDict[sortName] = sortComparatorFunction;
}

// Example code to add a bunch of sorting methods
self.addSort("Status",(a, b) => a["Status"].localeCompare(b["Status"]));
self.addSort("#", (a, b) => parseFloat(a["#"]) - parseFloat(b["#"]));
self.addSort("Name",(a, b) => a["Name"].localeCompare(b["Name"]));
self.addSort("Unique ID", (a, b) => parseFloat(a["Unique ID"]) - parseFloat(b["Unique ID"]));
self.addSort("Position",(a, b) => a["Position"].localeCompare(b["Position"]));


self.rebuildMultiSort = function (multiSortDict) {
    var sortList = Object.values(multiSortDict);

    var sortMethod = firstBy( sortList[0] ); // Grab the first entry to set the 'firstBy()' method

    for (var i = 1; i < sortList.length; i++) { // Starting at second position
        // I want to dynamically modify this line, being able to append as many 'thenBy()' statements as necessary
        // remove the semicolon from the previous sortMethod assignment
        // append the new 'thenBy()' to assignment
        sortMethod +=
            .thenBy(value);
    }
    return sortMethod;
}

使用上面的例子,理想情况下我会得到一个类似这样的代码行:

var s = firstBy( (a, b) => a["Status"].localeCompare(b["Status"]) )
  .thenBy( (a, b) => parseFloat(a["#"]) - parseFloat(b["#"]) )
  .thenBy( "Name",(a, b) => a["Name"].localeCompare(b["Name"]) )
  .thenBy( (a, b) => parseFloat(a["Unique ID"]) - parseFloat(b["Unique ID"]) )
  .thenBy( "Position",(a, b) => a["Position"].localeCompare(b["Position"]) )

有什么建议吗?

【问题讨论】:

    标签: javascript sorting dynamic code-generation


    【解决方案1】:

    在几乎所有情况下,都有比使用evil 更好的做事方式。

    我建议查找比较函数,例如

    const ordersLookup = {
      Status: (a, b) => a["Status"].localeCompare(b["Status"]),
      "#": (a, b) => parseFloat(a["#"]) - parseFloat(b["#"]),
      Name: (a, b) => a["Name"].localeCompare(b["Name"]),
      // ...
    }
    

    然后当你决定要排序时:

    let order = ["Name", "Status"];
    

    并从查找中选择适当的条目,并减少链:

    let s = order.reduce((a, e) => a.thenBy(ordersLookup[e]), firstBy(() => 0));
    

    let orderFunctions = order.map(e => ordersLookup[e]);
    let [firstInOrder, *restInOrder] = orderFunctions;
    let s = restInOrder.reduce((a, e) => a.thenBy(e), firstBy(firstInOrder));
    

    (最终结果应该相同,性能和可读性略有不同)。

    无需生成代码。

    另外,请注意 thenBy.js 知道如何处理按属性排序,因此您不需要自己编写大部分函数。 firstBy("Name").thenBy("#") 应该开箱即用。

    【讨论】:

    • 谢谢。这个例子在我修改后起作用了。这似乎正在做我正在寻找的工作!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-12-17
    • 2015-04-04
    • 1970-01-01
    • 2011-04-02
    • 1970-01-01
    • 2015-04-01
    • 1970-01-01
    相关资源
    最近更新 更多