Traversing an array of objects using D3.js

I'm attempting to create a row of bars that are all the same height and width based on their titles. I have an array of 100 objects, each with a key called results containing another array of 20 objects. This means I should end up with a row of 2000 bars, but I am only seeing 20 bars:

https://i.sstatic.net/0IO1l.png

Check out my Codepen example: https://codepen.io/ekilja01/pen/RdJqGO

The data consists of an array of objects with sub-objects called results, in this format:

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)}

This is the approach I am taking in my code:

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

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

    // Setting 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));

Answer №1

Great explanation of the issue and helpful CodePen example - it really aids in understanding your problem!

I have created a slightly modified version of your CodePen demo

The main issue was that, within each iteration of your for loop, you were selecting the first set of x (in this case 20) matching rect elements. This meant that with each iteration, except the first one, the data bindings would simply replace the same initial 20 rect elements with new data entries.

To address this problem, I made two changes:

  1. Replaced svg.selectAll('rect') with svg.selectAll('.rect_${i}'), which targets all elements with corresponding classes (.rect_0, .rect_1, etc.). By doing so, we avoid overriding previous rect elements.
  2. Added + xScale.range()[1] * i to the callback function of .attr('x'). This adjustment shifts each group of 20 rect elements to the right, preventing them from overlapping.

However, the second change results in a very wide chart. If you prefer the rows stacked vertically instead, I included a commented-out functionality using the height of your rect elements to achieve this.

Please let me know if this solution works for you!

Answer №2

When iterating through your loop, you are only appending elements the first time around. With 20 items in the data array and no rectangles in the SVG, all 20 items are entered initially. The second time, with 20 items in the data array and 20 rectangles in the SVG, the enter selection is empty as the new data is simply bound to the existing 20 rectangles.

An alternative approach can be taken where even though your data may not seem hierarchical, there is a hierarchical structure present. Aligning your data structure with your DOM structure simplifies the process. Consider restructuring your data like this:

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

With this new data structure, each object that needs to be mapped is now in one combined array for easy chart rendering. A simple enter cycle can then be implemented:

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;
  });

Typically, if you find yourself using a loop to enter elements into the DOM with d3, it might indicate a non-idiomatic use of the library. By avoiding the need for a for loop during element entry, scaling also becomes easier as we don't constantly update it while looping through the parent array. Scale domain and range only have to be set once.

You can view an updated codepen for reference.

Note: If you have 2000 elements, the resulting rectangles may be very small, potentially less than one pixel wide. Additionally, scaling the height of the bars should be considered separately.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Transfer data from a file to a PHP file using the XMLHttpRequest object in JavaScript and receive

I'm attempting to use AJAX to send an image file to a PHP file. The issue is that even though the script sends the object, my parser isn't able to retrieve the $_FILES["avatar"]["name"] and tmp_name values. Is there a method to transfer the file ...

Dealing with nested JSON structures in reqwest: A comprehensive guide

My current project involves utilizing reqwest library to execute a GET request on . Sending a request to a single-level json endpoint like is straightforward use std::collections::HashMap; fn main() { let body = reqwest::blocking::get("https://h ...

What's preventing my Angular list from appearing?

I am currently developing an Angular project that integrates Web API and entity framework code first. One of the views in my project features a table at the bottom, which is supposed to load data from the database upon view loading. After setting breakpoin ...

Tips for sending images as properties in an array of objects in React

I've been experimenting with various methods to display a background image underneath the "box" in styled components. How can I pass an image as a prop into the background image of the box with the image stored in the array of objects? I'm unsure ...

What could be causing my React components to not display my CSS styling properly?

If you're developing a React application and integrating CSS for components, ensure that you have included the style-loader and css-loader in your webpack configuration as shown below: module.exports = { mode: 'development', entry: &apo ...

Steps to remove a package from the npm registry

Is it feasible to eliminate or erase a complete module from the npm registry? Please be aware that using npm -f unpublish does not permit the deletion of packages older than 24 hours. ...

How can I change the text of a single button by clicking on it without affecting the other buttons that share the same

Currently, I am implementing a posting system where posts can be added using a button. The posts have two additional buttons - one to show/hide content and the other to delete content. I am utilizing jQuery's slideToggle event to toggle the visibility ...

AngularJS JSON data computation

As I delve into learning angularjs (not 2), one of the challenges I face is calculating the total amount for a customer order. The data I'm working with is structured as follows: var clients = [ { id: 1, jo ...

Error: Attempting to access a property named '_updatedFibers' on an undefined object is not possible due to a TypeError

I encountered the following error: Uncaught TypeError: Cannot read properties of undefined (reading '_updatedFibers') at requestUpdateLane (react-dom.development.js:25411:23) at updateContainer (react-dom.development.js:28810:14) at ReactDOMHydra ...

Ordering styles in Material UI

I've spent countless hours customizing this element to perfection, but the more I work on it, the more of a headache it gives me. The element in question is an outlined TextField, and my focus has been on styling its label and border. Initially, I th ...

Dealing with problems related to types in React and TypeScript createContext

Struggling to pass the todos (initial state) and addNewTodo (methods) using React Context hook and typescript. Despite trying multiple solutions, errors persist. Partial generics do not cause issues in the context component, but I encounter the error Cann ...

Tips for keeping JavaScript created checkboxes saved and accessible

Utilizing the "ajax" function within the script enables communication with the server by sending post or delete messages. The javascript code that includes ajax is responsible for dynamically adding checkboxes to the page. How can we ensure that the create ...

Step-by-step guide: Deploying your app to Heroku with Babel and ES6 support

I've been racking my brain trying to deploy the app on Heroku. The issue is with using ES6 along with Babel. I've come across numerous articles, but none have helped me resolve the problem. Even after building the app locally and attempting to ...

Is there a way for me to extend an absolute div to the full width of its grandparent when it is within an absolute parent div?

Here is a structured example of my HTML and CSS: <div class="grandparent"> <div class="row">...</div> <div class="absolute-parent"> <div class="absolute-child">...</div> ...

Having trouble locating the objects in the parent scope of an Angular directive

My custom directive needs to access the object $scope.$parent.users. When I use console.log $scope.$parent: myDirective.directive('scheduleItem', function(){ return { restrict: 'EA', link: function($sco ...

Ways to retrieve a specific item from a constantly changing knockout observableArray without employing a foreach loop

Why can I only access one property ('member_A') of an Element in an observableArray using an HTML <input>? I am attempting to add a new object of type abc() to the observableArray "list_of_abc" when the button "ADD To List of abc" is click ...

Execute a PHP script to retrieve data from a database based on the content of a text element

I'm currently working on a web-based system and I'm wondering if it's possible to retrieve a Name based on the number entered in a text box. Here is what I have attempted so far, but I know it's not functioning properly. Is there an alt ...

Arrange the list by first names in the array using Ionic 3

What is the process for arranging a list by firstName from an array? This is my code in my.ts file: initializeItems(){ this.items = [ { avatar: '../../assets/imgs/profile1.jpg', firstName:'Sterlian', lastName:'Victorian ...

How can we extract word array in Python that works like CryptoJS.enc.Hex.parse(hash)?

Is there a method in Python to convert a hash into a word array similar to how it's done in JavaScript? In JavaScript using CryptoJS, you can achieve this by using: CryptoJS.enc.Hex.parse(hash), which will provide the word array. I've searched ...

Is there a module loader in Angular.JS or do I have to rely on script tags for loading modules?

While using Angular JS, I have a desire to organize unrelated code in separate modules similar to AMD or CommonJS. However, my Google search for 'Angular.JS make new module' has not yielded any documentation on creating Angular.JS modules. There ...