2014-11-08 222 views
0

我搞乱了last.fm API。我拉着user's top artists data,它返回一个像这样的JSON:d3.js:嵌套数据 - 键返回undefined的所有数据

{ "artist": [ 
     { 
     "name": "Kanye West", 
     "playcount": "282", 
     "mbid": "164f0d73-1234-4e2c-8743-d77bf2191051", 
     "url": "http:\/\/www.last.fm\/music\/Kanye+West", 
     "streamable": "0", 
     "@attr": { 
      "rank": "1" 
     } 
     }, 
     { 
     "name": "Childish Gambino", 
     "playcount": "148", 
     "mbid": "7fb57fba-a6ef-44c2-abab-2fa3bdee607e", 
     "url": "http:\/\/www.last.fm\/music\/Childish+Gambino", 
     "streamable": "0", 
     "@attr": { 
      "rank": "2" 
     } 
     }, 

等。经过一番小提琴之后,我为第二个JSON(例如摇滚,流行音乐,嘻哈)拉动了第二个JSON,循环播放每位艺术家,并将他们最受欢迎的标签推送到原始数据集。所以,现在我的JSON的样子:

{ "artist": [ 
     { 
     "name": "Kanye West", 
     "playcount": "282", 
     "mbid": "164f0d73-1234-4e2c-8743-d77bf2191051", 
     "url": "http:\/\/www.last.fm\/music\/Kanye+West", 
     "streamable": "0", 
     "@attr": { 
      "rank": "1" 
     }, 
     "tag": "hip hop", 
     }, 
     { 
     "name": "Childish Gambino", 
     "playcount": "148", 
     "mbid": "7fb57fba-a6ef-44c2-abab-2fa3bdee607e", 
     "url": "http:\/\/www.last.fm\/music\/Childish+Gambino", 
     "streamable": "0", 
     "@attr": { 
      "rank": "2" 
     }, 
     "tag": "hip hop", 
     }, 

我想要做的下一件事是使用d3.nest使用每个标签(流派)为重点,以卷起我的数据。我正在关注这个tutorial。我的最终目标是制作一个sunburst,与内圈的流派(例如,你听多少嘻哈,独立歌曲,流行音乐?)和 - 也许点击 - 每个艺术家你听多少(例如Kanye West对幼稚甘比诺)。

的主要问题是在这里:

 var dataset = d3.nest() 
         .key(function(d) { 
          return d.tag; 
         }) 
         .entries(topArtists); 

key将返回undefined所有数据点。当我尝试其他可能的密钥(d.name,d.streamable)时,它们工作正常。我的数据按需要卷起来。所以这与tag有关 - 这是我推送到topArtists数据集的对象。我不确定发生了什么事。下面是我通过循环如何,并结合标签信息,艺术家信息:

//Merge 
    topArtists.forEach(function(d) { 

     //loop through each of the top 10 artists selected 
     //pull their mbid (musicbrainz id) and plug it into the var for tags    
     tags = 'http://ws.audioscrobbler.com/2.0/?method=artist.gettoptags&mbid='+ d.mbid + '&api_key=[my api key]&format=json'; 

      //pull that JSON 
      d3.json(tags, function(error, tag) { 

       if (error) { return console.log(error); }; 

       //capture only the most commonly used tag (artistTag) 
       //and also clean it up a bit 
       artistTag = tag.toptags.tag[0].name.toLowerCase().split('-').join(' '); 

       //push that string value to the original JSON 
       d["tag"] = artistTag; 


      }); // close tags JSON call 
     }); //close topArtists loop 

关闭这些循环后,如果我console.log(topArtists),这一切看起来很好 - 标签已被添加。 JSFiddle here

+0

问题是'd3.json'是异步的。它在'forEach'循环结束后运行。你可能想考虑一下[queue.js](https://github.com/mbostock/queue)。 – 2014-11-08 11:27:26

+0

谢谢,拉尔斯。我认为它可能是异步的 - 但是当我运行'forEach'循环和第二个JSON调用的'console.log(topArtists)* *外部时,它看起来很好 - 所有的'tags'都被追加。如果在下一行中,我尝试'd3.nest' - 它们会变成未定义的。事实上,即使我做了'd3.nest().key(function(d){console.log(topArtists);})',标签也显示出来了。由于某种原因,它们只是没有被卷起来。 – 2014-11-08 12:58:52

+0

所以'console.log()'打印一个对象不完全正确吗?在中,你点击它来展开属性等。点击的时间实际上是在评估时。也就是说,当你打印表达式时,它不在那里,但是当你点击它时,就是这样。在将数据放入数据之前和之后,您可以通过两次打印相同的表达式来查看。当您检查控制台上的这些对象时,即使它们在打印时明显不同,它们也是一样的。 – 2014-11-08 13:09:58

回答

0

感谢Lars's suggestion yesterday,我设法使用了Mike Bostock的queue.js库。万一别人运行到这个问题,这里是我遵循的步骤:

首先,我在<head>称为queue.js,在d3.js通话之后:

  <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> 
      <script src="http://d3js.org/queue.v1.min.js"></script> 

我想要做的主要事情是:

  1. 用户输入last.fm用户名。
  2. script拉起了一个JSON的user's top 10 (or 20, or whatever) artists
  3. 拉每位艺术家的“musicbrainz id”(一个唯一的数字标识符),并做一系列10(或20,或其他)d3.json调用来获得artist's top tags
  4. 将最常用的tag合并为每位艺术家的原创顶级艺术家JSON。 (艺术家可以有许多用户编写的tags,所以我只抽出最常用的一个。)
  5. 接下来 - 可视化。

在实践中,这意味着第一个顶尖的艺术家(topArtists)的做了d3.json呼叫,并且,内,一个queue.js呼叫 - 其中每个queue.defer()是什么在topArtists JSON的功能。之后,我可以循环,将tag添加到每个topArtists项目,然后使用d3.nest将我的数据排列在tags周围,如keys

下面是这一切的样子:

首先,外JSON呼吁topArtists。我这样做是为了将topArtists设置为JSON,否则,如果我尝试将它嵌套在queue.js之内,则最终会出现无限循环。

d3.json(top, function(error, json) { 

    if(error) { return console.log(error); } 

    topArtists = json.topartists.artist; 

接下来,以下this StackOverflow Q&A,我成立了一个queue()(1)再次从last.fm API(这是topArtists我会用前进来构建我的最终数据集)调用相同topArtists JSON ,然后(2)动态调用每个艺术家的tags API。

var q = queue() 
     .defer(d3.json, top); 

    topArtists.forEach(function(d) { 

      tags = 'http://ws.audioscrobbler.com/2.0/?method=artist.gettoptags&mbid='+ d.mbid + '&api_key=[my api key]&format=json' 

      q.defer(d3.json, tags); 

      }); 

所有这些JSONs已经被加载后,我遍历和(后检查的名称相匹配),每个艺术家的标签附加到topArtists数据,改名dataset

q.awaitAll(function() {   


      dataset = arguments[1][0].topartists.artist 

      for (var i = 1; i < arguments[1].length ; i++) { 

       var j = i - 1; 

       var artistName = arguments[1][0].topartists.artist[j].name; 
       var artistTagName = arguments[1][i].toptags['@attr'].artist; 

       if (artistName == artistTagName) { 

        artistTag = arguments[1][i].toptags.tag[0].name.toLowerCase().split('-').join(' '); 

        dataset[j]["tag"] = artistTag; 

       }; 

      }; //close for loop 

没有收出queue.awaitAll(),我现在可以使用d3.nest挽起我的数据。现在可以在可视化中使用dataset了;这也将被称为.awaitAll()

  dataset = d3.nest() 
       .key(function(d) { 
        return d.tag; 
       }) 
       .entries(dataset); 

      console.log(dataset);