【问题标题】:Access subkeys in multi-dimensional JavaScript object访问多维 JavaScript 对象中的子键
【发布时间】:2017-02-10 03:21:19
【问题描述】:

这确实是一个 JavaScript 问题,因为它涉及遍历多维 js 对象的元素并解释如何使用 Firefox 控制台对该对象的表示。

使用 d3.js/dc.js,我有两个折线图。当用户点击一个图表中的一个点时,我需要在其伴随图表上识别相同的点。两个图表都使用类似的 X 轴,即日期系列。日期将完全对应,因此图表 1 上的第 n 个数据点将对应于图表 2 上的第 n 个数据点 - 所以当用户点击图表 1 时,我有他们刚刚点击的数据点的数据,我想抓取“ y”数据来自图表 2 上的同一数据点(可通过在 x 轴上具有相同的“x”数据点来识别)。

我正在尝试遍历多维 JavaScript 对象(表示 d3.js 折线图 #2 上的数据点),但我不确定如何访问我需要的值。

这个循环:

lineChart.on('renderlet', function(lineChart) {
    var allDots = lineChart.selectAll('circle.dot');
    allDots.on('click', function(d) {
        var dot2find = d.x;
        for (var key in allDots) { //<=== THIS LOOP
            //at the moment, I am just cycling through chart 1 to find its own date
            if (allDots.hasOwnProperty(key)) {
                console.log(allDots[key]);
                //if (this_x_val == dot2find) alert('Found It');
        }
    }
}

结果:

[circle.dot, circle.dot, circle.dot, (etc, all the 600+ data elements)]

当我查看其中一个圆形对象时,我看到了这个(在 Firefox 控制台中):

当我在 Firefox 控制台中打开第一个 __data__ 对象时,我看到了我需要访问的元素:x(日期)值:

那么,在上面的 for 循环中,如何访问每个 circle.dot 对象中的 x(日期)值和 y(数字)值?

代码示例:

var startDate = new Date("2011-11-14T16:17:54Z");
var currDate = moment(startDate);
var cf = crossfilter([{date: startDate, quantity: 1}]);
AddData();

var timeDimension = cf.dimension(function(d){ return d.date; });
var totalGroup = timeDimension.group().reduceSum(function(d){ return d.quantity; });

var lineChart1 = dc.lineChart("#line-chart1")
    .brushOn(false)
    .width(800)
    .height(200)
    .elasticY(true)
    .x(d3.time.scale().domain([startDate, currDate]))
    .dimension(timeDimension)
    .group(totalGroup);
var lineChart2 = dc.lineChart("#line-chart2")
    .brushOn(false)
    .width(800)
    .height(200)
    .elasticY(true)
    .x(d3.time.scale().domain([startDate, currDate]))
    .dimension(timeDimension)
    .group(totalGroup);
var lineChart3 = dc.lineChart("#line-chart3")
    .brushOn(false)
    .width(800)
    .height(200)
    .elasticY(true)
    .x(d3.time.scale().domain([startDate, currDate]))
    .dimension(timeDimension)
    .group(totalGroup);

dc.renderAll();

lineChart1.on('renderlet', function(lineChart1) {
    lineChart1.selectAll('circle.dot').on('click', function(d) {
         alert('Chart One: X-axis (Date): ' +d.x+"\n\n"+ "Y-axis (Value): "+d.y);
         //How would I add the corresponding y-axis datapoint values for charts 2 and 3?
    });
});

d3.selectAll("circle.dot").on("mouseover", function(){
var thisDatum = d3.select(this).datum();
  d3.selectAll("circle.dot").filter(d=>d.x == thisDatum.x && d.y == thisDatum.y).attr("fill", "firebrick");
}).on("mouseout", function(){
	d3.selectAll("circle.dot").attr("fill", "teal")
})

for (var nn=1;nn<6;nn++){
	setTimeout(function(){
    AddData();
    lineChart1.x(d3.time.scale().domain([startDate, currDate]));
    lineChart2.x(d3.time.scale().domain([startDate, currDate]));
    lineChart3.x(d3.time.scale().domain([startDate, currDate]));
    dc.renderAll();
  },1000);
}

function AddData(){
    var q = Math.floor(Math.random() * 6) + 1;
    currDate = currDate.add('month', 1);
    cf.add( [{date: currDate.clone().toDate(), quantity: q}]);  
}
#line-chart3{margin-bottom:10px;}
.msg{font-family:Arial,Helvetica;font-size:1.2rem;color:brown;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.3.3/d3.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.1/crossfilter.min.js"></script>
<script src="http://dc-js.github.io/dc.js/js/dc.js"></script>
<link href="http://dc-js.github.io/dc.js/css/dc.css" rel="stylesheet"/>
<script src="http://cdnjs.cloudflare.com/ajax/libs/moment.js/2.7.0/moment.min.js"></script>

<div class="msg">Click a data point in chart 1:</div>
<div id="line-chart1"></div>
<div id="line-chart2"></div>
<div id="line-chart3"></div>
<div class="msg">Click on a data point in chart 1 -- an alert messagebox should pop-up with the corresp. datapoint values from all 3 line charts.</div>

【问题讨论】:

    标签: javascript d3.js dc.js


    【解决方案1】:

    您不需要任何类型的循环。

    要获取与被点击元素关联的属性,只需使用:

    d3.select(this).datum()
    

    在你的情况下,如果你想要 x 属性:

    d3.select(this).datum().x
    

    这是一个演示,向您展示如何做到这一点。在这个演示中,我用相同的数据绘制了两个 SVG。当您将鼠标悬停在一个圆圈上时,它会在另一个具有相同 xy 属性的 SVG 上绘制圆圈:

    var data = [{x:20, y:30},
    {x:40, y:60},
    {x:20, y:40},
    {x:10, y:90},
    {x:70, y:20},
    {x:60, y:90},
    {x:30, y:90},
    {x:90, y:10}];
    
    draw("#svg1");
    draw("#svg2");
    
    function draw(selector){
    
    var width = 250,
        height = 250;
    
    var svg = d3.select(selector)
        .append("svg")
        .attr("width", width)
        .attr("height", height);
    
    var xScale = d3.scaleLinear()
        .domain([0, 100])
        .range([30, width - 10]);
    
    var yScale = d3.scaleLinear()
        .domain([0,100])
        .range([height - 30, 10]);
    	
    var circles = svg.selectAll("foo")
    	.data(data)
    	.enter()
    	.append("circle");
    	
    circles.attr("r", 10)
    	.attr("fill", "teal")
    	.attr("cx", d=>xScale(d.x))
    	.attr("cy", d=>yScale(d.y));
    
    var xAxis = d3.axisBottom(xScale);
    var yAxis = d3.axisLeft(yScale);
    
    svg.append("g").attr("transform", "translate(0,220)")
        .attr("class", "xAxis")
        .call(xAxis);
    
    svg.append("g")
        .attr("transform", "translate(30,0)")
        .attr("class", "yAxis")
        .call(yAxis);
    
    }
    
    d3.selectAll("circle").on("mouseover", function(){
    var thisDatum = d3.select(this).datum();
      d3.selectAll("circle").filter(d=>d.x == thisDatum.x && d.y == thisDatum.y).attr("fill", "firebrick");
    }).on("mouseout", function(){
    	d3.selectAll("circle").attr("fill", "teal")
    })
    #svg1 {
      float: left;
    }
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <div id="svg1"></div>
    <div id="svg2"></div>

    【讨论】:

    • 嗨 Gerardo,答案写得非常好,但它并没有完全解决我要问的问题 (诚然使用了太多单词)。 在您的示例中,您正在应用在所有图表中为具有特定 X 值的所有数据点设置样式。我将如何修改它以从其他两个图表中的每个相应数据点收集“y”数据,同时识别每个数据来自哪个图表?
    • 比如我有chart1,chart2,chart3。用户单击图表 1 中 x = [2016 年 2 月 10 日下午 3:15](标准 js 日期)的数据点。我需要返回一个数组或对象,其中 y 值来自图表 2 和 3 上相同的 [Feb 10, 2016 at 3:15pm] 数据点,如下所示:{"chart2":"1705", "chart3":" 645"}
    • 嗯,这个问题与您帖子中的问题有点不同。在这种情况下,只需使用 getter 获取值并过滤数据数组。
    • 是的,事后看来,我意识到我埋葬了真正的问题“当用户点击图表 1 时,我有来自他们刚刚点击的数据点的数据,我想获取数据来自图表 2 上的同一数据点(可按日期识别)。” 在过多的序言和解释下。我了解过滤数据数组,但无法设想如何使用 getter 获取值。
    • 这是一个吸气剂:d3.select(this).datum().date。如果数据中有任何date 属性,则单击该元素时会得到该日期。
    【解决方案2】:

    要访问嵌套属性,例如'__data__' 对象的'__data__' 属性的'x' 属性,您只需链接属性取消引用:

    allDots[key].__data__.x
    

    allDots[key]['__data__']['x']
    

    (或其任何混合物)

    【讨论】:

    • 感谢 Ethan,我更新了问题以使其更加清晰。 Firefox 控制台具有显示对象属性的特定方式,并且该 _data_ 对象来自 Firefox 控制台,而不是来自图表的数据集。很奇怪,我知道,但你帮助我意识到我必须清楚这个对象是特定于 Firefox 控制台的。谢谢! BTW 和 FWIW,我通常更喜欢 Chrome 和它的 DevTools,但在这种情况下,Firefox 控制台更有帮助。提供 Chrome 控制台不提供的信息。
    • __data__ 是 d3 在内部用来将数据绑定到元素的。它不是由浏览器生成的。
    猜你喜欢
    • 2016-05-10
    • 1970-01-01
    • 2017-06-05
    • 2018-07-15
    • 2016-05-18
    • 1970-01-01
    • 2012-11-02
    • 2016-11-28
    • 1970-01-01
    相关资源
    最近更新 更多