What is the process for designating a data column as the key property in a line generator function?

One interesting feature of d3.js is the ability to access specific fields in a dataset using the syntax d.measure, where "d" accesses the data property and "measure" represents the field we want to retrieve. Inspired by code I discovered on bl.ocks.org, I decided to create a line chart. However, I wanted to enhance the function dsLineChart() so that I could dynamically specify the column I wish to use for visualizing values on the y-axis - essentially introducing a custom argument into dsLineChart(argument) that determines the chosen column.

Check out the example script below. My dataset comprises columns like "measure", "measure2", "measure3", and "measure4". While currently visualizing "measure" with d.measure, my goal is to call something like dsLineChart("measure2") to achieve the same functionality but with another column.

  • Example Dataset

var data = [
{group:"All",category:2011,measure:28107,measure2:53301,measure3:89015.40,measure4:138394},
{group:"All",category:2012,measure:39400,measure2:7001, measure3:55550.50,measure4:18004},
{group:"All",category:2013,measure:33894,measure2:690597,measure3:68289.50,measure4:17455},
{group:"All",category:2014,measure:55261,measure2:7172,measure3:73380.93,measure:418143} ];
  • Script Overview

I have provided a simple working script available at this link Fiddle D3js Line Chart

Thanks to valuable feedback from @GerardoFurtado, you can now see the revised script below which enables calling the function dsLineChart() with different arguments to generate line charts utilizing various measures (e.g., dsLineChart("measure2") vs. dsLineChart("measure")).

// dataset       
var lineChartData = [{
    category: 2011,
    measure: 28107,
    measure2: 53301,
    measure3: 89015.40,
    measure4: 138394
  },
  {
    category: 2012,
    measure: 39400,
    measure2: 7001,
    measure3: 55550.50,
    measure4: 18004
  },
  {
    category: 2013,
    measure: 33894,
    measure2: 690597,
    measure3: 68289.50,
    measure4: 17455
  },
  {
    category: 2014,
    measure: 55261,
    measure2: 7172,
    measure3: 73380.93,
    measure: 418143
  }
];

// layout
var margin = {
    top: 20,
    right: 10,
    bottom: 0,
    left: 50
  },
  width = 350 - margin.left - margin.right,
  height = 250 - margin.top - margin.bottom;

// function to draw linechart
function dsLineChart(selMeasure) {
    //convert object to array
  var data = d3.values(lineChartData);

  var property;
  var measures = [selMeasure];

  var xScale = d3.scaleLinear()
    .domain([0, data.length - 1])
    .range([0, width]);

  var yScale = d3.scaleLinear()
    .domain([0, d3.max(data, function(d) {
      return d[selMeasure];
    })])
    .range([height, 0])
    .range([height, 0]);

  var line = d3.line()
    .x(function(d, i) {
      return xScale(i);
    })
    .y(function(d) {
      return yScale(d[property]);
    });

  var svg = d3.select("#lineChart").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .attr("position", "absolute")
    .attr("top", "10px")
    .attr("left", "410px")

  var plot = svg
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
    .attr("id", "lineChartPlot");

  var paths = plot.selectAll(null)
    .data(measures)
    .enter()
    .append("path")
    .attr("class", "line")
    .attr("d", function(d) {
      property = d;
      return line(data)
    })
    .attr("stroke", "lightgrey")
    .attr("fill", "none")
    .attr("stroke-width", "4px");
}

dsLineChart("measure2");
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="lineChart"></div>

Answer №1

An elegant solution to this problem, and perhaps the most natural one, involves nesting your data so that the y value property can have a consistent name across all lines.

However, it is still possible to achieve what you are asking for. You can specify the scale passed to the line generator (refer to this answer) and determine which property to use for each method.

To make this work, we first define a variable:

var property;

This variable will be used in the line generator like so:

var line = d3.line()
    .x(function(d, i) {
        return xScale(i);
    })
    .y(function(d) {
        return yScale(d[property]);
    });

Next, we identify the actual properties. In this case, they are hardcoded, but you could dynamically extract them from the data:

var measures = ["measure", "measure2", "measure3", "measure4"];

We then bind this array as data:

var paths = plot.selectAll(null)
    .data(measures)
    .enter()
    .append("path")

The crucial step comes during the callback where you simply assign the value of property, crucial for the line generator:

.attr("d", function(d) {
    property = d;
    return line(data)
})

Combining these changes, here is your revised code snippet:

// Dataset
var data = [{
    group: "All",
    category: 2011,
    measure: 28107,
    measure2: 53301,
    measure3: 89015.40,
    measure4: 138394
  },
  {
    group: "All",
    category: 2012,
    measure: 39400,
    measure2: 7001,
    measure3: 55550.50,
    measure4: 18004
  },
  {
    group: "All",
    category: 2013,
    measure: 33894,
    measure2: 690597,
    measure3: 68289.50,
    measure4: 17455
  },
  {
    group: "All",
    category: 2014,
    measure: 55261,
    measure2: 7172,
    measure3: 73380.93,
    measure4: 418143
  }
];
var property;
var measures = ["measure", "measure2", "measure3", "measure4"];

// Layout
var margin = {
    top: 20,
    right: 10,
    bottom: 0,
    left: 50
  },
  width = 350 - margin.left - margin.right,
  height = 250 - margin.top - margin.bottom;

// Line chart drawing function
function dsLineChart() {

  var firstDatasetLineChart = data

  var xScale = d3.scaleLinear()
    .domain([0, firstDatasetLineChart.length - 1])
    .range([0, width]);

  var yScale = d3.scaleLinear()
    .domain([0, d3.max(firstDatasetLineChart, function(d) {
      return d.measure;
    })])
    .range([height, 0])
    .range([height, 0]);

  var line = d3.line()
    .x(function(d, i) {
      return xScale(i);
    })
    .y(function(d) {
      return yScale(d[property]);
    });

  var svg = d3.select("#lineChart").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .attr("position", "absolute")
    .attr("top", "10px")
    .attr("left", "410px")

  var plot = svg
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
    .attr("id", "lineChartPlot");

  var paths = plot.selectAll(null)
    .data(measures)
    .enter()
    .append("path")
    .attr("class", "line")
    .attr("d", function(d) {
      property = d;
      return line(data)
    })
    .attr("stroke", "lightgrey")
    .attr("fill", "none")
    .attr("stroke-width", "4px");
}

dsLineChart();
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="lineChart"></div>

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

Below are the steps to handle incorrect input after receiving only one letter:

H-I This is my input .centered-name { width: 80%; margin: auto; } .group { width: 100%; overflow: hidden; position: relative; } .label { position: absolute; top: 40px; color: #666666; font: 400 26px Roboto; cursor: text; transit ...

The V-if condition is specifically tailored for a single line or link

I am attempting to conceal a link ("myprofile") beneath an image in VUE.JS when the if-statement is not met. However, when I insert the v-if inside the vue-router or div tag, the entire image/div-tag becomes hidden. How can I implement an if-statement that ...

Struggling to retrieve the tagName attribute when clicking in JavaScript

I am trying to extract the tagName of any element within an iframe when it is clicked. Unfortunately, my JavaScript code is not working as expected. As a beginner in JavaScript, I would appreciate some guidance on where I might be going wrong. <div cl ...

Transferring data between different components in Angular 5

Presently, I am working with two distinct components within my project - a navigation component and a detail component. The navigation component contains several options or items that the user can select. Upon selection by the user, I am attempting to upda ...

Error alert: $.simpleWeather function not found

Currently, I am attempting to incorporate simpleWeather.js from the website simpleweatherjs.com into my own website. However, upon inserting the html code onto my website, an error message pops up: Uncaught TypeError: $.simpleWeather is not a function ...

Implementing fetch within a custom hook to display a loader within a return function

customFetch hook: import React, { useState, useEffect } from 'react'; const customFetch = (url, options) => { const [response, setResponse] = useState(null); const [error, setError] = useState(null); useEffect(() => { (async () ...

Error: The function cannot be found

Encountering Error in Chrome's Dev Tools: Uncaught TypeError: undefined is not a function The error points to a specific line in my main.js file. The following lines are triggering this error: jQuery("#signupfrm").fadeToggle('fast',functi ...

Tips for preventing repeated function calls in React applications

I'm currently developing a small React project and encountering an issue with function calls. The problem lies in updating the URL value and invoking the method through a button click to display the updated values. Oddly, when I click the button for t ...

Node: Incorporating variables into require modules

I wanted to inquire about the most effective way to pass variables into required modules using node js. Are there any recommended methods? And what should be avoided? So far, I am familiar with the following approaches: 1. Through function parameters my ...

Display the errors generated by Laravel when using async actions with Redux toolkit

In the slice, there is an implementation of the async thunk function utilizing axios for API calls. The HTTP client instance comes from another file where it's defined using axios.create() export const loginUser = createAsyncThunk("auth/fetchUser ...

Modify the CSS when CKEditor is in focus

Implementing CKEditor in a symfony project using the FOS\CKEditor-bundle 1.2. I want to style the entire element containing CKEditor with a border when it is focused, similar to how questions are written or answered on Stackoverflow. By referencing a ...

Jquery issue: Property or method is not supported by this object

Implementing a tree structure on my JSP Page using jQuery has been quite challenging. The tree structure necessitates the import of several jQuery files. However, upon running the JSP page, I encountered an error with the message "Object doesn't suppo ...

How can I retrieve the text from two DIV elements simultaneously using JS/jQuery?

Is there a way to loop through all <TD> elements in order to store the Title and Link from each element into separate variables using JavaScript / jQuery? Sample HTML: <td> <div class="class_Title row border-bottom" name="name_Title" i ...

jquery ajax not ready - responsetext empty - status code 0 - statustext error occurred

I encountered an error message stating: jquery ajax readystate 0 responsetext status 0 statustext error when providing this URL: url(http://www.tutorialspoint.com/prototype/prototype_ajax_response.htm), however, the same code works perfectly fine with thi ...

Next.js lacks proper Tree Shaking implementation for MUI

After setting up a react app with next.js, I noticed that the bundle size for the client significantly increased when importing MUI components. Specifically, there was a large module called @mui/base added to the bundle, even though I am only using three M ...

Running a Neo4j Cypher query with the help of jQuery

I've been attempting to create an Ajax call using jQuery to a Neo4j Server, both residing on the same machine. However, I keep encountering errors in the response. This is how my ajax call is written: var request = $.ajax({ type: "POST", url ...

Troubleshooting ng-class functionality in AngularJS

I am attempting to utilize AngularJS in order to change the class name of a div element. Despite following the guidance provided in this answer, I am encountering difficulties as the class name is not updating in my view. Below is the code from my view: ...

Starting a web application offline using npm: A beginner's guide

Asking about starting offline development for a web-oriented application may seem odd in today's constantly connected world, but I am truly fed up with this situation. Every time I initiate a project using modern web technologies and tools like webpa ...

Implementing route navigation in two components using a click button

To display the content of the Quiz component with the path "/quiz" after clicking a button and fetching the component with the path "/" is my goal. Here is the code snippet from the App.jsx file: <Router> <Routes> <Route ...

What is the best way to incorporate a changing variable within an htmx request?

One of the endpoints in my site requires an ID to be passed as a parameter. For example: mysite.com/product/{id}?limit=5 I'm wondering how to pass the 'id' variable in the hx-get attribute. I can utilize AlpineJS or vanilla JS for this tas ...