簡體   English   中英

將二維數組呈現為分層 html 表

[英]Render a 2d array as a hierarchical html table

我有一個這樣的數據結構,順序可以是隨機的

const table = [
  ['A', 'a', 0],
  ['A', 'a', 1],
  ['A', 'b', 0],
  ['B', 'a', 0],
  ['B', 'b', 1],
  ['B', 'b', 0],
]

這應該像這樣在 html 中呈現為分層表

<table>
  <tr><td rowspan="3">A</td><td rowspan="2">a</td><td>0</td></tr>
  <tr><td>1</td></tr>
  <tr><td>b</td><td>0</td></tr>
  <tr><td rowspan="3">B</td><td>a</td><td>0</td></tr>
  <tr><td rowspan="2">b</td><td>0</td></tr>
  <tr><td>1</td></tr>
</table>

我想知道什么是解決這個問題的有效方法。 考慮將列表轉換為樹,然后再轉換回列表,但我不確定這是解決此問題的最佳方法。

將每個項目轉換為一個具有 rowCount 的對象,並使用 0 表示前一個項目相同,因此不顯示它。 我不知道您使用的是什么視圖綁定庫,但在 Angular、Vue 或 React 中,綁定到表非常容易。 如果您在純 JavaScript 中執行此操作,則必須在循環中創建 DOM 元素。

 const table = [ ['A', 'a', 0], ['A', 'a', 1], ['A', 'b', 0], ['B', 'a', 0], ['B', 'b', 1], ['B', 'b', 0], ]; const rowSpan = rows => rows.map((row, rowIndex) => row.map((item, colIndex) => ({ data: item, rowSpan: countRows(rows, rowIndex, colIndex) })) ); const countRows = (rows, rowIndex, colIndex) => { if (rowIndex > 0 && rows[rowIndex - 1][colIndex] === rows[rowIndex][colIndex]) { return 0; } let count = 1; while (count + rowIndex < rows.length && rows[rowIndex + count][colIndex] === rows[rowIndex][colIndex]) { count++; } return count; }; const tableElement = document.createElement('table'); rowSpan(table).forEach(row => { const tr = document.createElement('tr'); tableElement.appendChild(tr); row.forEach(item => { if (item.rowSpan) { const td = document.createElement('td'); td.rowSpan = item.rowSpan; td.innerHTML = item.data.toString(); tr.appendChild(td); } }); }); document.body.appendChild(tableElement);
 td { border: 1px solid black; vertical-align: top; padding: 3px 10px; }

這似乎是一個很好的問題。 我的回答可能有點長,請耐心等待。

我的方法基本上是在 JS 級別處理所有數據操作,然后一次將表附加到 DOM,而不會在整個操作邏輯中弄亂 DOM 元素。

基本思想是轉換

var table = [ ['A', 'a', 0]
            , ['A', 'a', 1]
            , ['A', 'b', 0]
            , ['B', 'a', 0]
            , ['B', 'b', 1]
            , ['B', 'b', 0],
            ];

進入

[ [ { text: 'A', span: 3, bool: true  }
  , { text: 'a', span: 2, bool: false }
  , { text: 0,   span: 1, bool: false }
  ]
,
  [ { text: 'A', span: 0, bool: true  }
  , { text: 'a', span: 0, bool: false }
  , { text: 1,   span: 1, bool: false }
  ]
,
  [ { text: 'A', span: 0, bool: true  }
  , { text: 'b', span: 1, bool: false }
  , { text: 0,   span: 1, bool: false }
  ]
,
  [ { text: 'B', span: 3, bool: true  }
  , { text: 'a', span: 1, bool: false }
  , { text: 0,   span: 1, bool: false }
  ]
,
  [ { text: 'B', span: 0, bool: true  }
  , { text: 'b', span: 2, bool: false }
  , { text: 1,   span: 1, bool: false }
  ]
,
  [ { text: 'B', span: 0, bool: true  }
    { text: 'b', span: 0, bool: false }
    { text: 0,   span: 1, bool: false }
  ]
]

為了獲取這個數據,我在兩個地方使用了一個稍微不正統的方法,我認為這應該存在於 JS 中,但是......我們必須自己制作。 Array.prototype.zipWith()將接受一個函數和另一個數組,並將提供的函數應用於兩個數組的相應元素。 如果數組的長度不同,那么當最短的所有項目都用完時它會停止。

[1,2,3,4].zipWith((x,y) => x + y, [5,6,7])   // [6,8,10]
[1,2,3].zipWith((x,y) => x + y, [5,4,3,2,1]) // [6,6,6]

這可能是這樣的

[].zipWith || Object.defineProperty( Array.prototype
                                   , "zipWith"
                                   , {value: function(f,a){
                                               var l = a.length;
                                               return this.reduce((r,e,i) => i < l ? ( r.push(f(e,a[i]))
                                                                                     , r
                                                                                     )
                                                                                   : r, []);
                                             }
                                     }
                                   );

我們可以將下面的代碼在return層分成兩個reduce階段,進行相應的研究。

  1. reduce階段提供臨時數據。
  2. reduce階段創建包含表的 DOM 片段。

正如您可能已經注意到我的縮進也有些不尋常,但相信我,一旦您將代碼復制粘貼到您最喜歡的文本編輯器上,它就會更容易閱讀......我希望:)

所以這里的代碼帶有稍微不同的數據集,以便在“隨機性”開始時顯示它的行為......如果我正確理解 OP。

 [].zipWith || Object.defineProperty( Array.prototype , "zipWith" , {value: function(f,a){ var l = a.length; return this.reduce((r,e,i) => i < l ? (r.push(f(e,a[i])),r) : r, []); } } ); var table = [ ['A', 'a', 1] , ['A', 'a', 1] , ['A', 'b', 1] , ['B', 'b', 1] , ['B', 'a', 0] , ['B', 'b', 1] , ['B', 'b', 1] , ['B', 'b', 0] ]; function makeTable([r,...rs]){ var f = new DocumentFragment(), t = f.appendChild(document.createElement("table")), z = (f,s) => f.text === s ? ( f.span += 1 , f ) : ( f.bool = false , { text: s , span: 1 , bool: false } ); t.id ="table-with-spans"; return rs.reduce( function(rs,r){ var b = rs[rs.length-1].zipWith((f,s) => f.text === s && f.bool, r) .some(b => b); return ( rs.push(b ? rs[rs.length-1].zipWith(z,r) : r.map(d => ({ text: d , span: 1 , bool: true }) ) ) , rs ) } , [r.map(d => ({ text: d , span: 1 , bool: true }) ) ] ) .reduce( function(t,r){ var tds = r.filter(td => td.span), tde; t.appendChild(document.createElement("tr")) .append(...tds.map(function(td){ tde = document.createElement("td"); tde.textContent = td.text; tde.rowSpan = td.span; td.span = 0; return tde; }) ); return t; } , t ).parentNode; } document.getElementById("table-container") .appendChild(makeTable(table));
 #table-with-spans { width : 50vw; text-align : center; border : 1px solid black; border-collapse: separate; border-spacing : 2px; margin : 0 auto; } tr{ height : 2.5vw; } td { padding: 2px; border: 1px solid black; vertical-align: middle; }
 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width"> <title>repl.it</title> <link href="style.css" rel="stylesheet" type="text/css" /> <script src="script.js" async></script> </head> <body> <div id="table-container"> </div> </body> </html>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM