JavaScript — Array.map () и d3.selectAll (). data.enter ()

Вызов

Поскольку ваш вопрос говорит о более чистом коде и меньшем количестве строк, перед вами стоит задача.

Я создал 3 кнопки, по одной для каждой группы в массиве data (и четвертую для всех групп). Когда вы нажимаете кнопку, он фильтрует данные и соответственно обновляет диаграмму:

 var h = 250, w = 500, p = 40; var svg = d3.select("body") .append("svg") .attr("width", w) .attr("height", h); var g1 = svg.append("g") var g2 = svg.append("g") var data = [{ group: "foo", value: 14, name: "A" }, { group: "foo", value: 35, name: "B" }, { group: "foo", value: 87, name: "C" }, { group: "foo", value: 12, name: "D" }, { group: "bar", value: 84, name: "E" }, { group: "bar", value: 65, name: "F" }, { group: "bar", value: 34, name: "G" }, { group: "baz", value: 98, name: "H" }, { group: "baz", value: 12, name: "I" }, { group: "baz", value: 43, name: "J" }, { group: "baz", value: 66, name: "K" }, { group: "baz", value: 42, name: "L" }]; var color = d3.scaleOrdinal(d3.schemeCategory10); var xScale = d3.scaleLinear() .range([0, w - p]) .domain([0, d3.max(data, function(d) { return d.value })]); var yScale = d3.scaleBand() .range([0, h]) .domain(data.map(function(d) { return d.name })) .padding(0.1); var axis = d3.axisLeft(yScale); var gY = g2.append("g").attr("transform", "translate("   p   ",0)") .call(axis); draw(data); function draw(data) { yScale.domain(data.map(function(d) { return d.name })) var rects = g1.selectAll("rect") .data(data, function(d) { return d.name }) rects.enter() .append("rect") .attr("x", p) .attr("y", function(d) { return yScale(d.name) }) .attr("width", 0) .attr("height", yScale.bandwidth()) .attr("fill", function(d) { return color(d.group) }) .transition() .duration(1000) .attr("width", function(d) { return xScale(d.value) }); rects.transition() .duration(1000) .attr("x", p) .attr("y", function(d) { return yScale(d.name) }) .attr("width", function(d) { return xScale(d.value) }) .attr("height", yScale.bandwidth()) .attr("fill", function(d) { return color(d.group) }); rects.exit() .transition() .duration(1000) .attr("width", 0) .remove(); gY.transition().duration(1000).call(axis); }; d3.selectAll("button").on("click", function() { var thisValue = this.id; var newData = thisValue === "all" ? data : data.filter(function(d) { return d.group === thisValue; }); draw(newData) }); 
 {amp}lt;script src="https://d3js.org/d3.v4.min.js"{amp}gt;{amp}lt;/script{amp}gt; {amp}lt;button id="foo"{amp}gt;Foo{amp}lt;/button{amp}gt; {amp}lt;button id="bar"{amp}gt;Bar{amp}lt;/button{amp}gt; {amp}lt;button id="baz"{amp}gt;Baz{amp}lt;/button{amp}gt; {amp}lt;button id="all"{amp}gt;All{amp}lt;/button{amp}gt; {amp}lt;br{amp}gt; {amp}lt;br{amp}gt; 

Более чистый код основывается на мнении, но мы можем легко измерить его размер.

Таким образом, вот проблема: попробуйте создать код, который делает то же самое, но с использованием подхода map() , то есть без привязки каких-либо данных. Сделайте все переходы, которые я делаю здесь. Код, который вы попытаетесь воссоздать, — это весь код внутри функции on("click") .

После этого мы сравним размер вашего кода и размер идиоматических вариантов «вход», «обновление» и «выход».

Заключение

Подход map() может сэкономить вам 2 линии при первом рисовании элементов. Тем не менее, это сделает вещи ужасно громоздкими.

D3 означает Data-Driven Documents

Самая мощная функция в D3, которая дает само название библиотеки, — это ее способность связывать данные с элементами DOM. Делая это, вы можете манипулировать этими элементами DOM на основе связанных данных несколькими способами, например (но не ограничиваясь ими):

  • Сортировать
  • Фильтр
  • Перевести
  • Стиль
  • присоединять
  • Удалить

И так далее…

Если вы не привязываете данные к элементам DOM, например, используя подход map() в вашем вопросе (который аналогичен forEach() ), вы можете сохранить пару строк в начале, но вы закончите с неловким кодом, чтобы иметь дело с последним. Давай увидим это:

Введите выбор

Теперь давайте попробуем тот же код, но с использованием идиоматического выделения «enter»:

 var h = 250, w = 500, p = 40; var svg = d3.select("body") .append("svg") .attr("width", w) .attr("height", h); var data = [{ group: "foo", value: 14, name: "A" }, { group: "foo", value: 35, name: "B" }, { group: "foo", value: 87, name: "C" }, { group: "foo", value: 12, name: "D" }, { group: "bar", value: 84, name: "E" }, { group: "bar", value: 65, name: "F" }, { group: "bar", value: 34, name: "G" }, { group: "baz", value: 98, name: "H" }, { group: "baz", value: 12, name: "I" }, { group: "baz", value: 43, name: "J" }, { group: "baz", value: 66, name: "K" }, { group: "baz", value: 42, name: "L" }]; var color = d3.scaleOrdinal(d3.schemeCategory10); var xScale = d3.scaleLinear() .range([0, w - p]) .domain([0, d3.max(data, function(d) { return d.value })]); var yScale = d3.scaleBand() .range([0, h]) .domain(data.map(function(d) { return d.name })) .padding(0.1); svg.selectAll(null) .data(data, function(d) { return d.name }) .enter() .append("rect") .attr("x", p) .attr("y", function(d) { return yScale(d.name) }) .attr("width", function(d) { return xScale(d.value) }) .attr("height", yScale.bandwidth()) .attr("fill", function(d) { return color(d.group) }); var axis = d3.axisLeft(yScale); var gY = svg.append("g").attr("transform", "translate("   p   ",0)") .call(axis); 
 {amp}lt;script src="https://d3js.org/d3.v4.min.js"{amp}gt;{amp}lt;/script{amp}gt; 

Как видите, он немного длиннее предыдущего метода map() , на 2 строки длиннее.

Тем не менее, это на самом деле связывает данные с этими прямоугольниками. Если вы console.log выделите D3 одного из этих прямоугольников, вы увидите что-то вроде этого (в Chrome):

 {amp}gt; Selection {amp}gt; _groups: Array(1) {amp}gt; 0: Array(1) {amp}gt; 0: rect {amp}gt; __data__: Object group: "bar" name: "G" value: 34 

Поскольку этот код на самом деле привязывает данные к элементам DOM, вы можете манипулировать ими таким образом, который будет громоздким (если не сказать больше), используя подход map() . Я покажу это в следующем фрагменте, который будет использоваться для предложения задачи.

Карта () подход

Вот очень простой код, использующий большую часть вашего фрагмента для создания гистограммы с использованием подхода map() :

 var h = 250, w = 500, p = 40; var svg = d3.select("body") .append("svg") .attr("width", w) .attr("height", h); var data = [{ group: "foo", value: 14, name: "A" }, { group: "foo", value: 35, name: "B" }, { group: "foo", value: 87, name: "C" }, { group: "foo", value: 12, name: "D" }, { group: "bar", value: 84, name: "E" }, { group: "bar", value: 65, name: "F" }, { group: "bar", value: 34, name: "G" }, { group: "baz", value: 98, name: "H" }, { group: "baz", value: 12, name: "I" }, { group: "baz", value: 43, name: "J" }, { group: "baz", value: 66, name: "K" }, { group: "baz", value: 42, name: "L" }]; var color = d3.scaleOrdinal(d3.schemeCategory10); var xScale = d3.scaleLinear() .range([0, w - p]) .domain([0, d3.max(data, function(d) { return d.value })]); var yScale = d3.scaleBand() .range([0, h]) .domain(data.map(function(d) { return d.name })) .padding(0.1); data.map(function(d, i) { svg.append("rect") .attr("x", p) .attr("y", yScale(d.name)) .attr("width", xScale(d.value)) .attr("height", yScale.bandwidth()) .attr("fill", color(d.group)); }); var axis = d3.axisLeft(yScale); var gY = svg.append("g").attr("transform", "translate("   p   ",0)") .call(axis); 
 {amp}lt;script src="https://d3js.org/d3.v4.min.js"{amp}gt;{amp}lt;/script{amp}gt; 

Вроде бы хороший результат, все бары есть. Однако нет никаких данных, связанных с этими прямоугольниками. Сохраните этот код, мы будем использовать его в следующем задании .

Chalenge # 2

Эта задача № 2 может быть еще более интересной для демонстрации возможностей D3, когда речь идет о привязке данных.

В этом новом коде я сортирую исходный массив данных через 1 секунду и перерисовываю диаграмму. Затем, нажав на кнопку «обновить», я привязываю другой массив данных к столбцам.

Хорошая вещь здесь — это ключевая функция, которая связывает каждую полосу с каждой точкой данных, используя, в данном случае, свойство name :

 .data(data, function(d) { return d.name }) 
Понравилась статья? Поделиться с друзьями:
JavaScript & TypeScript
Adblock
detector