J'essaie de créer une rangée de barres de hauteur et de largeur égales en fonction du titre. Ayant un tableau de 100 objets, chacun contient une clé appelée résultats qui lui-même et un tableau de 20 autres objets, donc le résultat final doit être un rangée de 2000 barres, mais je n'obtiens que 20 barres :

wrong output

Codepen : https://codepen.io/ekilja01/pen/RdJqGO

Les données sont un tableau d'objets contenant un tableau d'objets appelé résultats, au format suivant :

0:
page: 1
results: Array(20)
0: {vote_count: 17968, id: 19995, video: false, vote_average: 7.4, title: "Avatar", …}
...
length: 20

1: {page: 2, total_results: 406130, total_pages: 20307, results: Array(20)}
2: {page: 3, total_results: 406130, total_pages: 20307, results: Array(20)}

Voici mon approche:

d3.json('data.json').then(data => {

  console.log(data);
  for (let i = 0; i < data.length; i++) {


    //x and y domain
    xScale.domain(data[i].results.map(d => d.title));
    yScale.domain(data[i].results.map(d => d.original_title));

    svg.selectAll('rect')
      .data(data[i].results)
      .enter()
      .append('rect')
      .style('fill', 'red')
      .attr('width', xScale.bandwidth())
      .attr('height', 70)
      .attr('x', function (d) {
        return xScale(d.title);
      })
      .attr('y', function (d) {
        return yScale.bandwidth() + 175;
      });

  }
}).catch(error => console.log(error));
2
Edgar Kiljak 18 mars 2019 à 16:52

2 réponses

Meilleure réponse

Dans votre boucle, vous n'ajoutez des éléments que la première fois dans la boucle : il y a 20 éléments dans le tableau de données, aucun rectangle dans le svg, donc les 20 éléments sont entrés. La deuxième fois, il y a 20 éléments dans le tableau de données, 20 rectangles dans le svg, donc la sélection d'entrée est vide, les nouvelles données sont simplement liées aux 20 rectangles existants.

Comme approche alternative à l'autre réponse, vos données ne semblent pas être hiérarchiques, mais vous avez une structure hiérarchique. Si votre structure de données est la même que votre structure DOM, alors ce sera beaucoup plus simple. Restructurons un peu vos données :

var combined = [];
for (let i = 0; i < data.length; i++) {
  combined.push(...data[i].results);
}

Maintenant nos données sont :

[
  {vote_count: 17968, id: 19995, video: false, vote_average: 7.4, title: "Avatar", …,}
  {...},
  {...},
  ...

Et tous les objets à mapper sont dans un tableau, un élément du tableau, un élément à ajouter au graphique. Maintenant, nous pouvons faire un simple cycle d'entrée :

svg.selectAll('rect')
  .data(combined)
  .enter()
  .append('rect')
  .style('fill', 'red')
  .attr('width', xScale.bandwidth())
  .attr('height', 70)
  .attr('x', function (d) {
    return xScale(d.title);
  })
  .attr('y', function (d) {
    return yScale.bandwidth() + 175;
  });

Généralement, si vous avez besoin d'une boucle pour entrer des éléments dans le DOM, vous n'utilisez pas le d3. En évitant la boucle for pour entrer des éléments, la mise à l'échelle devient également plus facile car nous ne la mettons pas constamment à jour lorsque nous parcourons le tableau parent. Le domaine d'échelle et la plage ne doivent être définis qu'une seule fois.

Voici un codepen mis à jour.

Maintenant, 2000 éléments donneront lieu à de très petits rectangles, probablement moins d'un pixel de large, et je ne sais pas pourquoi vous redimensionnez la hauteur des barres ainsi, mais ce sont des questions différentes.

2
Andrew Reid 18 mars 2019 à 16:38

Belle description du problème et CodePen - il est facile de vous aider !

J'ai fait une version légèrement modifiée de la vôtre lien CodePen

Fondamentalement, ce qui s'est passé, c'est que pour chaque itération de votre boucle for, vous sélectionniez le premier x (dans votre cas 20) correspondant à rect éléments. Cela signifie que chaque itération, après la première, remplacerait simplement les liaisons de données aux mêmes 20 éléments rect par les 20 entrées de données de l'itération actuelle.

J'ai fait deux modifications pour corriger cela:

  1. svg.selectAll('rect') changé en svg.selectAll('.rect_${i}'), qui sélectionne tous les éléments avec la classe correspondante (.rect_0, .rect_1, etc.). Cela empêche d'écraser les éléments rect précédents.
  2. Ajout de + xScale.range()[1] * i à la fonction de rappel de .attr('x'). Cela déplace chaque lot de 20 éléments rect vers la droite, pour s'assurer que les éléments rect ne sont pas superposés.

Le point 2 donne cependant un tableau très large. Au cas où vous voudriez que les lignes se superposent, j'ai ajouté des fonctionnalités (dans un commentaire) en utilisant la hauteur de vos éléments rect pour ce faire.

Faites-moi savoir si cela vous aide!

3
matthias.rocks 19 mars 2019 à 09:18