d3.nest()使用多个相同级别的键嵌套数组元素(创建重复项)

时间:2014-02-11 11:43:05

标签: javascript arrays d3.js nested

我正在使用此处发布的Tate集合中的数据:https://github.com/tategallery/collection

我正试图通过运动对艺术家进行分组。艺术家的每个JSON对象都有一个属性“运动”,它是零个或多个元素的数组。

例如,Vito Acconci与概念艺术和表演艺术有关:

"movements": [
    {
      "era": {
        "id": 415, 
        "name": "20th century post-1945", 
        "workCount": 3604
      }, 
      "id": 421, 
      "name": "Conceptual Art", 
      "workCount": 478
    }, 
    {
      "era": {
        "id": 415, 
        "name": "20th century post-1945", 
        "workCount": 3604
      }, 
      "id": 436, 
      "name": "Performance Art", 
      "workCount": 81
    }
] 

我现在想通过他们的动作对艺术家进行分组,理想情况下使用d3.nest()。我的问题是,我如何处理与几个动作相关的艺术家?我希望它们在每个相关的运动中出现一次。

这是一个带有数据摘录的JSFiddle:http://jsfiddle.net/jwFZV/

2 个答案:

答案 0 :(得分:0)

像这样解决:http://jsfiddle.net/fEyZ6/6/

想象我实际上需要一个与d3.nest()提供的结构不同的结构,所以这更有意义。

可能比必要的复杂一点,但我需要能够通过字符串属性或对象数组进行重构。我正在使用标识符来选择对象属性作为数组键。考虑使用地图,但我正在更改对象,因此键不匹配。

优化建议仍然受到欢迎。

var Hierarchy = function(data, accessor) {

    var data = data,
        accessor = accessor,
        identifier = "name",
        hierarchy = false,
        rebuild = false;

    this.hierarchy = function() {

        if(hierarchy && !rebuild) {
            return hierarchy;
        }

        hierarchy = [];
        var keys = [];

        if( !accessor) {
            console.log("ERROR no accessor for hierarchy");
            return false;
        }


        for (var i in data) {

            var parents = accessor.call(data, data[i]);

            if( Object.prototype.toString.call( parents ) != "[object Array]") {

                parents = [parents];

            };

            parents.forEach( function(element) {

                if(typeof element === "object") {

                    p = keys[element[identifier]];

                } else {

                    p = keys[element];

                }

                if (!p) {

                    p = {
                        element: element,
                        children: []
                    };
                }

                p.children.push(data[i]);


                if(typeof element === "object") {

                    keys[element[identifier]] = p;

                } else {

                    keys[element] = p;

                }

            } )


        }

        for (var i in keys) {

            if( typeof keys[i].element != "object" ) {
                keys[i].element = {
                    name: keys[i].element
                }
            }

            keys[i].element.children = keys[i].children;
            hierarchy.push(keys[i].element);

        }

        rebuild = false;

        return hierarchy;
    }

    // accessors
    this.data = function(_) {
        if(!arguments.length) return data;
        data = _;
        rebuild = true;
    };

    this.identifier = function(_) {
        if(!arguments.length) return identifier;
        identifier = _;
        rebuild = true;
    };

    this.accessor = function(_) {
        if(!arguments.length) return accessor;
        accessor = _;
        rebuild = true;
    };


}

答案 1 :(得分:0)

您的数据几乎已经嵌套,这就是问题所在。

查看https://github.com/mbostock/d3/wiki/Arrays#wiki-d3_pairs上的示例(在d3.pairs之后)。

该示例以三个相似对象的平面数据集开始,并且在嵌套之后,对象被复制以同时属于多个组。这比预先嵌套的JSON对象更通用,因为引用了对象而不是创建为新对象。

在您的数据集中,动作被复制到多个艺术家之下,但如果两个艺术家具有相同的动作,则无法使用==进行比较。它总会返回false。

另一种方法是将每个对象集合(艺术家,动作......)发送为它自己的平面对象数组。对象可以通过某些键属性或其相关对象的索引列表显示它们与其他对象的关系。

表格方法还允许您将数据作为CSV或TSV发送到客户端。如果你正在处理一个巨大的数据集,你就会知道对象键占用了多少空间,并且它们对于每个对象都是相同的!表头解决了这个问题。在处理整数和短字符串值时,这会变得更加明显。

首先,您应该删除重复的对象并分离出不同的数据类型。这将为您提供一个更清晰的起点。

这是一对手枪形状的片段开始。他们的工作

var artists = data;

var movements = data.reduce(function(object, artist){
  artist.movements = artist.movements.map(function(movement){
    object[movement.id] = object[movement.id] || movement;
    return movement.id;
  }).join(',');
  return object;
},{});

var eras = d3.values(movements).reduce(function(object, movement){
  var eid = movement.era.id;
  object[eid] = object[eid] || movement.era;
  movement.era = eid;
  return object;
},{});