0

I built an app which consumes data from a redis channel(sellers) with socketio and push the data in realtime to the frontend. The dataset could contain up to a thousand rows so I'm thinking about using a datatable to represent the data in a clean way. The table elements will be updated regularly, but there will be no rows to add/remove, only updates.

The problem I'm facing is that I don't know which would be the proper way to implement it due to my inexperience in the visualization ecosystem. I've been toying with d3js but I think It'll be too difficult to have something ready quickly and also tried using the datatables js library but I failed to see how to make the datatable realtime.

This is the code excerpt from the front end:

socket.on('sellers', function(msg){
  var seller = $.parseJSON(msg);
  var sales = [];
  var visits = [];
  var conversion = [];
  var items = seller['items'];

  var data = [];
  for(item in items) {
    var item_data = items[item];
    //data.push(item_data)
    data.push([item_data['title'], item_data['today_visits'], item_data['sold_today'], item_data['conversion-rate']]);
  }

  //oTable.dataTable(data);

  $(".chart").html("");
  drawBar(data);
});
4

1 回答 1

2

使用 d3 解决您的问题既简单又优雅。今天早上我花了一点时间来创建一个你可以适应自己需求的小提琴:

http://jsfiddle.net/CelloG/47nxxhfu/

要使用 d3,您需要了解它如何将数据连接到 html 元素。查看http://bost.ocks.org/mike/join/以获取作者的简要说明。

小提琴中的代码是:

var table = d3.select('#data')

// set up the table header
table.append('thead')
    .append('tr')
    .selectAll('th')
        .data(['Title', 'Visits', 'Sold', 'Conversion Rate'])
    .enter()
        .append('th')
        .text(function (d) { return d })

table.append('tbody')

// set up the data
// note that both the creation of the table AND the update is
// handled by the same code.  The code must be run on each time
// the data is changed.

function setupData(data) {
    // first, select the table and join the data to its rows
    // just in case we have unsorted data, use the item's title
    // as a key for mapping data on update
    var rows = d3.select('tbody')
        .selectAll('tr')
        .data(data, function(d) { return d.title })

    // if you do end up having variable-length data,
    // uncomment this line to remove the old ones.
    // rows.exit().remove()

    // For new data, we create rows of <tr> containing
    // a <td> for each item.
    // d3.map().values() converts an object into an array of
    // its values
    var entertd = rows.enter()
        .append('tr')
            .selectAll('td')
                .data(function(d) { return d3.map(d).values() })
            .enter()
                .append('td')

    entertd.append('div')
    entertd.append('span')

    // now that all the placeholder tr/td have been created
    // and mapped to their data, we populate the <td> with the data.

    // First, we split off the individual data for each td.
    // d3.map().entries() returns each key: value as an object
    // { key: "key", value: value}
    // to get a different color for each column, we set a
    // class using the attr() function.

    // then, we add a div with a fixed height and width
    // proportional to the relative size of the value compared
    // to all values in the input set.
    // This is accomplished with a linear scale (d3.scale.linear)
    // that maps the extremes of values to the width of the td,
    // which is 100px

    // finally, we display the value.  For the title entry, the div
    // is 0px wide
    var td = rows.selectAll('td')
        .data(function(d) { return d3.map(d).entries() })
        .attr('class', function (d) { return d.key })

    // the simple addition of the transition() makes the
    // bars update smoothly when the data changes
    td.select('div')
        .transition()
        .duration(800)
        .style('width', function(d) {
            switch (d.key) {
                case 'conversion_rate' :
                    // percentage scale is static
                    scale = d3.scale.linear()
                        .domain([0, 1])
                        .range([0, 100])
                    break;
                case 'today_visits': 
                case 'sold_today' :
                    scale = d3.scale.linear()
                    .domain(d3.extent(data, function(d1) { return d1[d.key] }))
                    .range([0, 100])
                    break;
                default:
                    return '0px'
            }
            return scale(d.value) + 'px'
        })
    td.select('span')
        .text(function(d) {
            if (d.key == 'conversion_rate') {
                return Math.round(100*d.value) + '%'
            }
            return d.value
        })
}

setupData(randomizeData())

d3.select('#update')
    .on('click', function() {
        setupData(randomizeData())
    })

// dummy randomized data: use this function for the socketio data
// instead
//
// socket.on('sellers', function(msg){
//  setupData(JSON.parse(msg).items)
// })
function randomizeData() {
    var ret = []
    for (var i = 0; i < 1000; i++) {
        ret.push({
            title: "Item " + i,
            today_visits: Math.round(Math.random() * 300),
            sold_today: Math.round(Math.random() * 200),
            conversion_rate: Math.random()
        })
    }
    return ret
}
于 2015-05-16T17:28:11.930 回答