В d3.js, пропустите append() для нулевых данных - программирование
Подтвердить что ты не робот

В d3.js, пропустите append() для нулевых данных

Я рисую линейный график из маленьких круглых пуль. Тем не менее, данные имеют в нем дыры, которые в моем массиве представлены нулем. Естественно, везде, где нет данных, не должно быть кругов. Но метод d3 append() добавляет их в любом случае. Как мне избежать этого?

Здесь jsFiddle mockup точно воспроизводит мою проблему.

Я заинтересован в том, чтобы не иметь серию кругов, лежащих на оси X моего графа, так как все они являются нулями.

Соответствующий код из ссылки jsfiddle:

svg.selectAll('circle').data(values).enter()
  .append('circle')// <-- I don't want to do this for null's
  .attr('fill', '#c00')
  .attr('r', 3)
  .attr('cx', xi)
  .attr('cy', yFlipped)
4b9b3361

Ответ 1

Один из вариантов - представлять ваши данные по-разному, чтобы вы не зависели от индекса для вычисления координаты x. Например, если вы представляли каждую дату в качестве объекта (например, {x: 0, y: 0.2840042}), вы могли бы вычислить координату x как x(d.x), а не x(i).

Другим вариантом было бы установить радиус в ноль, когда значение равно null, поэтому круги скрыты: circle.attr("r", function(d) { return d == null ? 0 : 3; }). Или вы можете скрыть круги: circle.style("display", function(d) { return d == null ? "none" : null; }).

Вы также можете удалить нулевые элементы после их добавления: circle.filter(function(d) { return d == null; }).remove(). Это будет работать для первоначального создания, но я бы не рекомендовал его, потому что индекс изменился бы, если вы повторно перечислили элементы позже.

Ответ 2

Попробуйте этот шаблон, который может удалить или скрыть круги.

// Step 1: hides all circles which are "null"
d3.selectAll(".yourItem")
.data(data)
.enter()
    .append("circle")
    .attr("visibility", function(d,i){
        if(yourFunction(d) == null) return "hidden";
})

// Step 2: optional, deletes all circles which are "hidden"
d3.selectAll("circle[visibility=hidden]").remove();

Ответ 3

Самый простой вариант - отфильтровать нули из передаваемых данных в .data(…), увеличивая данные для поддержания индекса:

svg.selectAll('circle')
  .data(values
    .map(function(v, idx) { return v == null? null : { idx: idx, value: v })
    .filter(function(v) { return v != null })
  )
  .enter()
    .append('circle')
    .attr('fill', '#c00')
    .attr('r', 3)
    .attr('cx', function(d) { return d.idx * 10 }) // or whatever
    .attr('cy', function(d) { return d.value.y }) // or whatever

Обратите внимание, что вы также можете следовать этому шаблону отдельных подэлементов, даже если они не являются естественными списками. Например, рассмотрим ситуацию, когда вы хотите условно добавить второй круг:

var circles = [
    { color: 'red', cx: 30, cy: 30, subCircleColor: 'blue' },
    { color: 'blue', cx: 60, cy: 60, subCircleColor: 'green' },
    { color: 'green', cx: 90, cy: 90 },
];

// Create a group which will hold the circles, since the result will
// be:
//    <g class="circles">
//      <circle color="{{ color }}" ... />
//      <circle class="sub-circle" color="{{ subCircleColor }}" ... />
//    </g>
var circlesGroups = svg.selectAll("g.circles")
    .data(circles)
    .enter()
        .append("g").attr({"class": "circles"})

// Add the first circle to the group
circlesGroups
    .append("circle").attr({
        "fill": function(d) { return d.color },
        "r": 20,
        "cx": function(d) { return d.cx },
        "cy": function(d) { return d.cy },
    })

// If there is a subCircleColor, add the second circle to the group
circlesGroups.selectAll("circle.sub-circle")
    .data(function(d) {
        if (d.subCircleColor)
            return [d];
        return [];
    })
    .enter()
        .append("circle").attr({
            "class": "sub-circle",
            "fill": function(d) { return d.subCircleColor; },
            "r": 10,
            "cx": function(d) { return d.cx },
            "cy": function(d) { return d.cy },
        })

Fiddle: http://jsfiddle.net/3d6e648k/

Ответ 4

Вот путь, если вы хотите никогда не добавлять его в первую очередь. Я использовал оператор if внутри .each(), а затем d3.select(this), чтобы добавить его к текущему элементу.

var data = [9, 0, 7, 0, 5, 0, 3, 0, 1];

var svg = d3.select('#svg').append('svg').attr({'viewBox': '-10 -10 99 20'});

svg.selectAll('g').data(data).enter().append('g').each(function (d,i) {
 if(d){
  d3.select(this).append('circle').attr({r:5,cx:(i*9)});
 }else{
  d3.select(this).remove();
 }
});

Вот bin http://jsbin.com/qojifug/edit?js,output

Ответ 5

Просто отфильтруйте его.

values.filter(function(el){return el !== null;})