【问题标题】:Organzing data with Javascript into a table使用 Javascript 将数据组织到表格中
【发布时间】:2018-03-17 16:33:51
【问题描述】:

我有大约 5k 的文档需要整理和组织,从 mongodb 中提取,通过 express 提取到 ejs 模板。我已经能够成功地将文档转换为 ejs 模板,但是我在如何处理项目的第二部分 - 组织数据方面遇到了困难。

以下是我的数据外观的示例。我的目标是在最左列列出所有缺陷位置(总共大约 30 个),并根据每年和每月计算缺陷位置发生的次数。我不反对使用框架或 jquery。我能想到的唯一一件事就是为每个单元格分配一个函数,该函数遍历数组以查看它是否符合该单元格的要求。 (但这似乎与编程的本意背道而驰)。最后,我想添加图表,但在这一点上似乎真的很牵强。需要补充一点 - 这不是我将使用的唯一日期范围,它们可以追溯到 2012 年到 2017 年。

  [{
    "_id": "59cee5ce8ffdc0134854f0c1",
    "repairorder": 7192822,
    "month": 2,
    "year": 2015,
    "defectlocation": "MB"
  }, {
    "_id": "59cee5ce8ffdc0134854f0c2",
    "repairorder": 7192822,
    "month": 5,
    "year": 2015,
    "defectlocation": "COVER/HOUSING"
  }, {
    "_id": "59cee5ce8ffdc0134854f0c3",
    "repairorder": 7192822,
    "month": 2,
    "year": 2015,
    "defectlocation": "MB"
  }, {
    "_id": "59cee5ce8ffdc0134854f0c5",
    "repairorder": 7192822,
    "month": 3,
    "year": 2015,
    "defectlocation": "TOUCH PAD"
  }, {
    "_id": "59cee5ce8ffdc0134854f0c6",
    "repairorder": 7192822,
    "month": 4,
    "year": 2015,
    "defectlocation": "MB"
  }]

下面是我需要它的显示方式:

  -----------------------------------------------------------------------
  Defect Location | 01-2015 |  02-2015 |  03-2015 |  04-2015 |  05-2015 |
  -----------------------------------------------------------------------
  MB              |         |    2     |          |    1     |          |
  -----------------------------------------------------------------------
  Touch Pad       |         |          |     1    |          |          |
  -----------------------------------------------------------------------
  Cover/ Housing  |         |          |          |          |     1    |
  -----------------------------------------------------------------------
  TOTAL           |         |     2    |     1    |     1    |     1    |

【问题讨论】:

    标签: javascript jquery mongoose datatables frameworks


    【解决方案1】:

    听起来您的问题是如何组织数据以计算每个时间段内每个缺陷位置的实例。这将起作用。您可以缩短它,但我尝试使用易于阅读的 js。从终点开始,您可以使用任何表格库,例如 datatables.net 或手动创建一个 html 表格,如 Mikey 的回答所示。希望这会有所帮助。

    *** 更新:我最初忽略了 TOTAL 行。我已经更新了我的答案以包含它(并作为数组中的最后一行)。我还添加了先按年然后按月对列进行排序(因为您的期望最终结果示例以这种方式显示)并将代码分成两个函数,希望使其更具可读性。

    var data = [{
        "_id": "59cee5ce8ffdc0134854f0c1",
        "repairorder": 7192822,
        "month": 2,
        "year": 2015,
        "defectlocation": "MB"
      }, {
        "_id": "59cee5ce8ffdc0134854f0c2",
        "repairorder": 7192822,
        "month": 5,
        "year": 2015,
        "defectlocation": "COVER/HOUSING"
      }, {
        "_id": "59cee5ce8ffdc0134854f0c3",
        "repairorder": 7192822,
        "month": 2,
        "year": 2015,
        "defectlocation": "MB"
      }, {
        "_id": "59cee5ce8ffdc0134854f0c5",
        "repairorder": 7192822,
        "month": 3,
        "year": 2015,
        "defectlocation": "TOUCH PAD"
      }, {
        "_id": "59cee5ce8ffdc0134854f0c6",
        "repairorder": 7192822,
        "month": 4,
        "year": 2015,
        "defectlocation": "MB"
      }];
    
      var tableData = {};
      var totalRow = {};
      var allCols = [];
      var asArray = [];
    
      tallyInstances();
      prepForTable();
    
      function tallyInstances () {
        var i;
        var monthDate;
        for (i = 0; i < data.length; i++) {
          monthDate = data[i].month.toString() + '-' + data[i].year.toString();
          allCols.indexOf(monthDate) < 0 ? allCols.push(monthDate) : null;
          if (!tableData[data[i].defectlocation]) {
            tableData[data[i].defectlocation] = {}; // if our tableData object doesn't have a property for this defect location yet then add it and make it an object
          }
          if (tableData[data[i].defectlocation][monthDate]) {
            tableData[data[i].defectlocation][monthDate] ++; // if this defect location object has a property for this year/month combination already then increment it's value by one
          } else {
            tableData[data[i].defectlocation][monthDate] = 1; // if this defect location object does not have a property for this year/month combination yet then create the property and give it a value of 1
          }
          totalRow[monthDate] ? totalRow[monthDate] ++ : totalRow[monthDate] = 1; // ternary operator saying if the totalRow object already has a property for this year/month combination then increment it's value by one, otherwise create it and give it a value of 1
        }
      }
    
      function prepForTable () {
        allCols.sort(function(a, b) {
          var aParts = a.split("-");
          var bParts = b.split("-");
          var x = {
            month : aParts[0],
            year  : aParts[1]
          };
          var y = {
            month : bParts[0],
            year  : bParts[1]
          };
          var n = x.year - y.year;
          if (n !== 0) {
            return n;
          }
          return x.month - y.month;
        });
        var keys = Object.keys(tableData);
        var e;
        var rowObj;
        for (e = 0; e < keys.length; e++) {
          rowObj = {};
          rowObj["Defect Location"] = keys[e];
          var a;
          for (a = 0; a < allCols.length; a++) {
            rowObj[allCols[a]] = tableData[keys[e]][allCols[a]] ? tableData[keys[e]][allCols[a]] : '';
          }
          asArray.push(rowObj);
        }
        rowObj = {};
        rowObj["Defect Location"] = "TOTAL";
        var o;
        for (o = 0; o < allCols.length; o++) {
          rowObj[allCols[o]] = totalRow[allCols[o]] ? totalRow[allCols[o]] : '';
        }
        asArray.push(rowObj);
      }
    
      console.log("tableRows: ", JSON.stringify(asArray, null, 4));
      /*
     tableRows:  [
        {
            "Defect Location": "MB",
            "2-2015": 2,
            "3-2015": "",
            "4-2015": 1,
            "5-2015": ""
        },
        {
            "Defect Location": "COVER/HOUSING",
            "2-2015": "",
            "3-2015": "",
            "4-2015": "",
            "5-2015": 1
        },
        {
            "Defect Location": "TOUCH PAD",
            "2-2015": "",
            "3-2015": 1,
            "4-2015": "",
            "5-2015": ""
        },
        {
            "Defect Location": "TOTAL",
            "2-2015": 2,
            "3-2015": 1,
            "4-2015": 1,
            "5-2015": 1
        }
    ]
    
    */
    

    【讨论】:

    • 这把它钉在了头上!感谢您使您的代码如此明确。我仍在学习语言工作原理的所有复杂性,似乎我将学习基础知识一段时间。像 Mikey 的代码一样,我正在评论每一行以更好地理解该过程。我唯一理解问题的部分是以 = {};, ++; 结尾的 if 语句和 =1;
    • @JeffFasulkey 除了注释掉行之外,如果您还没有这样做,请在控制台日志中加入,以便更好地了解正在发生的事情。我回去看看是否可以添加一些 cmets 以使您提到的那些行更容易理解。
    • 我非常感谢您的帮助。我有一个问题:我所有的“数据”都来自猫鼬作为一个对象并呈现为一个 ejs 模板。然后我将该数据对象转换为 json 并将其传递给您提供的代码。我的问题是,我什至需要转换它吗?如果答案是否定的,我确信需要进行更改,但我想知道我是否采取了太多步骤才能达到最终结果。
    • @JeffFasulkey 在没有看到代码的情况下很难给你一个高质量的答案。您不会想要 JSON.stringify() 它,因为那样您将无法将其作为 javascript 对象进行交互。如果它已经是一个 JSON 字符串,那么您可能正在使用 JSON.parse() 处理它,您需要继续这样做。我希望我没有误解您的问题,如果您认为我是,或者您需要进一步的帮助,请随时发表评论。
    • 这个项目有了新的生命,可以说我想对列进行横向总计,例如 2015 年的 MB 总数。我遇到了一些困难。我已经回到了数组的基础知识,并且正在尝试重写该项目,因为即使您的代码确实有帮助,但我不知道我是否能够像从头开始一样掌握它。如果这有任何意义
    【解决方案2】:

    主要思想是将您的数据重新构建成一个数据结构,您可以轻松地使用它来构建您的理想表。

    让我们将对象数组更改为嵌套对象:

    var deflects = {
        'MB': {
            '02-2015': 2,
            '04-2015': 1
        },
        'TOUCH PAD':  {
            '03-2015': 1
        }
        'COVER/HOUSING': {
            '05-2015': 1
        },
        'TOTAL': {
            '02-2015': 2,
            '03-2015': 1,
            '04-2015': 1,
            '05-2015': 1
        }
    };
    

    var data = [{
        "_id": "59cee5ce8ffdc0134854f0c1",
        "repairorder": 7192822,
        "month": 2,
        "year": 2015,
        "defectlocation": "MB"
      }, {
        "_id": "59cee5ce8ffdc0134854f0c2",
        "repairorder": 7192822,
        "month": 5,
        "year": 2015,
        "defectlocation": "COVER/HOUSING"
      }, {
        "_id": "59cee5ce8ffdc0134854f0c3",
        "repairorder": 7192822,
        "month": 2,
        "year": 2015,
        "defectlocation": "MB"
      }, {
        "_id": "59cee5ce8ffdc0134854f0c5",
        "repairorder": 7192822,
        "month": 3,
        "year": 2015,
        "defectlocation": "TOUCH PAD"
      }, {
        "_id": "59cee5ce8ffdc0134854f0c6",
        "repairorder": 7192822,
        "month": 4,
        "year": 2015,
        "defectlocation": "MB"
    }];
    
    function pad(n, width, z) {
         z = z || '0';
        n = n + '';
        return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
    }
    
    // PART 1: create object i.e. dictionary
    
    // create a dictionary where each key is the defectlocation
    // and its value is another dictionary;
    
    // each inner dictionary will have a key for each date found
    // and its value as a counter
    var defects = {};
    
    // let's create another inner dictionary for tallying
    defects.TOTAL = {};
    
    data.forEach(function (d) {
        if (!defects.hasOwnProperty(d.defectlocation)) {
            defects[d.defectlocation] = {};
        }
    
        var date = pad(d.month, 2) + '-' + d.year;
        if (!defects[d.defectlocation].hasOwnProperty(date)) {
            defects[d.defectlocation][date] = 0;
        }
        defects[d.defectlocation][date]++;
      
        if (!defects.TOTAL.hasOwnProperty(date)) {
            defects.TOTAL[date] = 0;
        }
        defects.TOTAL[date]++;
    });
    
    var dates = Object.keys(defects.TOTAL).sort();
    
    // you would pass deflects and dates into your view
    
    // PART 2: build the table (view)
    
    var html = '<table>';
    html += '<tr>';
    html += '<th>Defect Location</th>';
    dates.forEach(function (date) {
         html += '<th>' + date + '</th>';
    });
    html += '</tr>';
    
    ['MB', 'TOUCH PAD', 'COVER/HOUSING', 'TOTAL'].forEach(function (location) {
        html += '<tr>';
        html += '<td>' + location + '</td>';
        dates.forEach(function (date) {
            html += '<td>' + (defects[location][date] || '') + '</td>';
        });
        html += '</tr>';
    })
    
    html += '</table>';
    
    document.getElementById('preview').innerHTML = html;
    table {
      border-collapse: collapse;
    }
    th, td {
      border: 1px solid #000;
    }
    th:first-child {
      text-align: left; 
    }
    td:not(:first-child) {
      text-align: center;
    }
    &lt;div id="preview"&gt;&lt;/div&gt;

    虽然第 2 部分中的 HTML 生成器不是 EJS 格式,但您可以按照相同的逻辑轻松构建它。 PART 1 是重要的部分。

    另外,填充逻辑取自 answer

    【讨论】:

    • 谢谢!这真的很好用。通过评论所有内容,我可以更好地了解它的工作原理。
    • 嘿,我要感谢你在这方面的帮助,我最终分解了你的代码并从中学到了很多东西,这帮助我创建了整个应用程序。
    • @JeffFasulkey 没问题!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-25
    • 2019-01-31
    • 1970-01-01
    相关资源
    最近更新 更多