如何使用 d3.js 按不同的变量对 table 进行排序?

How can I use d3.js to sort a table by different variables?

    data = [
      {
        "name": "Dave",
        "dept": "Marketing",
        "region": "South",
        "items": 28
      },
      {
        "name": "Amy",
        "dept": "IT",
        "region": "West",
        "items": 46
      },
      {
        "name": "John",
        "dept": "Sales",
        "region": "North",
        "items": 35
      },
      {
        "name": "Sarah",
        "dept": "Communications",
        "region": "North",
        "items": 13
      }
    ]

    drawTable(data)
    sortTable(data)

    function drawTable(data) {

      // not working
      // when redrawing table, trying to re-initialize sort function
      sortTable(data)

      d3.select('#data-table').remove()

      let table = d3.select('#table').append('div').attr('id', 'data-table')

      let row = table.selectAll('.row')
        .data(data)
        .enter()
        .append('div')
        .attr('class', 'row')

      let grid = row
        .append('div')
        .attr('class', 'grid')

      let name = grid.append('div')
        .attr('class', 'box')
        .html(d => d.name)

      let dept = grid.append('div')
        .attr('class', 'box')
        .html(d => d.dept)

      let region = grid.append('div')
        .attr('class', 'box')
        .html(d => d.region)

      let items = grid.append('div')
        .attr('class', 'box')
        .html(d => d.items)

    }

    function sortTable(data) {
      d3.selectAll(".name,.dept,.region,.items").on('click', function () {
        let sortWhich = d3.select(this).attr("class")
        console.log('sortWhich', sortWhich)

        let tableSort
        if (sortWhich == "name") {
          tableSort = data.sort((a, b) => d3.ascending(a.name, b.name))
        } else if (sortWhich == "dept") {
          tableSort = data.sort((a, b) => d3.ascending(a.dept, b.dept))
        } else if (sortWhich == 'region') {
          tableSort = data.sort((a, b) => d3.ascending(a.region, b.region))
        } else {
          tableSort = data.sort((a, b) => d3.ascending(a.items, b.items))
        }

        // Can I combine the above and pass a variable to a sort function? This is not working:
        // let tableSort = data.sort((a, b) => a.sortWhich - b.sortWhich)

        drawTable(tableSort)
      })
    }
html,
    body {
      height: 100%;
      margin: 0;
    }

    .grid {
      display: flex;
      flex-wrap: wrap;
      flex-direction: row;
    }

    .grid>div {
      display: flex;
      flex-basis: calc(100%/4 - 28px);
      justify-content: center;
      flex-direction: column;
      padding: 10px;
    }

    .box {
      margin: 0;
    }

    .box {
      color: #000;
      border: .5px solid #ccc;
    }

    .hrbox {
      background-color: #333;
      color: #fff;
    }
<!DOCTYPE html>
<html lang="en">

<head>
  <script src="https://d3js.org/d3.v6.min.js"></script>
</head>
<body>
  <div id="table">
    <div class="grid">
      <div class="box hrbox name">
        <div>name</div>
      </div>
      <div class="box hrbox dept">
        <div>dept</div>
      </div>
      <div class="box hrbox region">
        <div>region</div>
      </div>
      <div class="box hrbox items">
        <div>items</div>
      </div>
    </div>
    <div id="data-table"></div>
  </div>
</body>

我使用 d3.js 创建了一个 flexbox table,它提取 json 数据来填充它。 我希望能够按 header 行对每一列进行排序,为此我编写了一个排序函数。但是,我现在一次只能对一列进行排序。然后,如果我刷新页面,我可以对不同的列进行排序。我希望能够在不需要刷新页面的情况下进行切换。所以 - 按名称排序,然后决定按项目排序,然后按区域排序,以查看数据的不同视图。我错过了什么?

如果有任何反馈,我将不胜感激。

[我还想要一个排序函数,我将一个变量传递给它,而不是拥有所有这些条件。我不确定这是否可能。 (绝对不是我主要关心的问题。)但是把它放在那里]

谢谢!

如果您查看控制台,您会发现 sortWhich 始终类似于 "box hrbox whatever",因此所有条件都将失败,代码将转到 else 在底部。

最简单的解决方案是使用 includes():

if (sortWhich.includes("name")) etc...

这是您的代码,其中包含该更改:

data = [{
    "name": "Dave",
    "dept": "Marketing",
    "region": "South",
    "items": 28
  },
  {
    "name": "Amy",
    "dept": "IT",
    "region": "West",
    "items": 46
  },
  {
    "name": "John",
    "dept": "Sales",
    "region": "North",
    "items": 35
  },
  {
    "name": "Sarah",
    "dept": "Communications",
    "region": "North",
    "items": 13
  }
]

drawTable(data)
sortTable(data)

function drawTable(data) {

  // not working
  // when redrawing table, trying to re-initialize sort function
  sortTable(data)

  d3.select('#data-table').remove()

  let table = d3.select('#table').append('div').attr('id', 'data-table')

  let row = table.selectAll('.row')
    .data(data)
    .enter()
    .append('div')
    .attr('class', 'row')

  let grid = row
    .append('div')
    .attr('class', 'grid')

  let name = grid.append('div')
    .attr('class', 'box')
    .html(d => d.name)

  let dept = grid.append('div')
    .attr('class', 'box')
    .html(d => d.dept)

  let region = grid.append('div')
    .attr('class', 'box')
    .html(d => d.region)

  let items = grid.append('div')
    .attr('class', 'box')
    .html(d => d.items)

}

function sortTable(data) {
  d3.selectAll(".name,.dept,.region,.items").on('click', function() {
    let sortWhich = d3.select(this).attr("class")
    console.log('sortWhich', sortWhich)

    let tableSort
    if (sortWhich.includes("name")) {
      tableSort = data.sort((a, b) => d3.ascending(a.name, b.name))
    } else if (sortWhich.includes("dept")) {
      tableSort = data.sort((a, b) => d3.ascending(a.dept, b.dept))
    } else if (sortWhich.includes('region')) {
      tableSort = data.sort((a, b) => d3.ascending(a.region, b.region))
    } else {
      tableSort = data.sort((a, b) => d3.ascending(a.items, b.items))
    }

    // Can I combine the above and pass a variable to a sort function? This is not working:
    // let tableSort = data.sort((a, b) => a.sortWhich - b.sortWhich)

    drawTable(tableSort)
  })
}
html,
body {
  height: 100%;
  margin: 0;
}

.grid {
  display: flex;
  flex-wrap: wrap;
  flex-direction: row;
}

.grid>div {
  display: flex;
  flex-basis: calc(100%/4 - 28px);
  justify-content: center;
  flex-direction: column;
  padding: 10px;
}

.box {
  margin: 0;
}

.box {
  color: #000;
  border: .5px solid #ccc;
}

.hrbox {
  background-color: #333;
  color: #fff;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <script src="https://d3js.org/d3.v6.min.js"></script>
</head>

<body>
  <div id="table">
    <div class="grid">
      <div class="box hrbox name">
        <div>name</div>
      </div>
      <div class="box hrbox dept">
        <div>dept</div>
      </div>
      <div class="box hrbox region">
        <div>region</div>
      </div>
      <div class="box hrbox items">
        <div>items</div>
      </div>
    </div>
    <div id="data-table"></div>
  </div>
</body>

这里有一些问题:

  • sort() 就地排序数组,不需要 tableSort 变量
  • 再次删除绘制 table 的元素是 不是 惯用的 D3,只需使用 enter/update/exit 选择。

关于你的第二个问题,如果你有正确的变量名,你可以只使用括号表示法,删除所有 if...else 部分:

data.sort((a, b) => d3.ascending(a[sortWhich], b[sortWhich]))