使用Clojurescript中的变异

时间:2013-06-29 18:16:25

标签: json d3.js clojurescript

我正在尝试使用d3.js从JSON结构中绘制一些网格来学习Clojurescript的工作原理。我正在使用strokes访问d3。

JSON看起来像这样:

[[{"players":{"0":{"rep":0},"1":{"rep":0}}},{"players":{"0":{"rep":0},"1":{"rep":0}}},
  {"players":{"0":{"rep":0},"1":{"rep":0}}},{"players":{"0":{"rep":0},"1":{"rep":0}}}],
 [{"players":{"0":{"rep":0},"1":{"rep":0}}},{"players":{"0":{"rep":0},"1":{"rep":0}}},
  {"players":{"0":{"rep":0},"1":{"rep":0}}},{"players":{"0":{"rep":0},"1":{"rep":0}}}],
 [{"players":{"0":{"rep":0},"1":{"rep":0}}},{"players":{"0":{"rep":0},"1":{"rep":0}}},
  {"players":{"0":{"rep":0},"1":{"rep":0}}},{"players":{"0":{"rep":0},"1":{"rep":0}}}],
 [{"players":{"0":{"rep":0},"1":{"rep":0}}},{"players":{"0":{"rep":0},"1":{"rep":0}}}, 
  {"players":{"0":{"rep":0},"1":{"rep":0}}},{"players":{"0":{"rep":0},"1":{"rep":0}}}]]

它代表一个4乘4的网格。我正在尝试向单元格添加值,例如高度,宽度,x和y坐标,这样就可以将数据传递给d3进行绘制。

例如它看起来像这样:

[[{"width":32,"height":32,"x":0,"y":0,"value":{"players":{"0":{"rep":0},"1":{"rep":0}}}},
  {"width":32,"height":32,"x":32,"y":0,"value":{"players":{"0":{"rep":0},"1":{"rep":0}}}},...

通常我会使用转换函数在结构上进行映射,以将单元格从当前值转换为新形式,但这种方法似乎不起作用。我已经尝试了map-indexed: (map-indexed #(doto %2 (aset "width" %1)) row),但这似乎无法正确转换值。我很可能错误地访问或设置了值。

代码的当前迭代如下所示:

(defn board->grid [grid-width grid-height board square]
  (let [x-length (count board)
        y-length (count (first board))
        same (min (/ grid-width x-length) (/ grid-height y-length))
        grid-item-width (if square same (/ grid-width x-length))
        grid-item-height (if square same (/ grid-height y-length))
        start-x (/ grid-item-width 2)
        start-y (/ grid-item-height 2)
        values (array)
        grid (array)
        data (js->clj board :keywordize-keys true)]
        (doseq [x (range x-length)
                y (range y-length)]
          (let [current-cell (aget data y x)]
          (.log js/console (apply str (aset (aget data y x) "a" "b")))
          (.push grid (aget data y x))))
        (.text ($ :#status) grid)))

任何帮助将不胜感激!或者更好的是,更好的方法的建议,我不禁觉得我正在做这个有点错误!

3 个答案:

答案 0 :(得分:5)

在clojure和clojurescript中使用可变性的基本经验法则是“不要”。 JS数组和对象没有实现函数依赖的大多数协议来完成它们的工作。例如,普通的js数组是不可序的!使用不可变数据结构完成大部分工作,并仅在需要与其他库接口时转换为可变等效项。

还有一些特定于阵列的附加功能:into-arrayto-arrayagetasetamapareducealength。 (见cheat sheet。)

如果您希望坚持使用可变数据结构,那么您在closure librarygoog.arraygoog.object中可能会发现许多goog.structs个功能。 (请记住,clojurescript包含谷歌闭包库!)

您也可以在任何地方使用此表单:

(defn amap2d [arr f]
  (doseq [x (range (alength arr))
          y (range (alength (aget A x)))
         :let [cell (aget A y x)]]
    (f x y cell)))

但我认为你会更快乐地处理js->clj,处理数据,然后在将其传递给d3之前clj->js

答案 1 :(得分:4)

我发现您在aget上使用data,而data是ClojureScript数据结构。 aget仅用于处理类似Array的JavaScript对象。

答案 2 :(得分:1)

我不熟悉clojurescript;但有些建议:

使用vanilla javascript将数组数组格式化为具有坐标的对象的平面数组:

var grid = [];
board.forEach(function(row, y){ 
  row.forEach(function(d, x){ 
    d.y = y; d.x = x; grid.push(d); }); });

要显示电路板,您可能希望使用以下内容:

svg.selectAll("rect")
  .data(grid).enter().append("svg:rect")
    .attr("x", function(d){ return xScale(d.x); })
    .attr("y", function(d){ return yScale(d.y); })
    .attr("height", rectHeight)
    .attr("width",  rectWidth)

请注意,方块的实际位置未存储在对象中,使用比例更容易跟踪:

xScale = d3.scale.linear()
  .domain(d3.extent(grid.map(function(d){ return d.x; }).
  .range(0, svgWidth);