見出し画像

D3のスケール関数を自作関数で模倣するやり方

こんにちわ。nap5です。

D3のスケール関数を自作関数で模倣するやり方一つを紹介したいと思います。

CodePenに動作確認できるサンプルを置きました。

https://www.skypack.dev/view/d3


シンプルな入出力のチュートリアルが豊富でいい感じです。

https://www.d3indepth.com/scales/


それぞれ実行結果は同じになります

画像1


コードを抜き出したものも貼り付けます。

ポイントは関数を変数に代入しておき、実行時に実引数(item.x, 4)を与えて、呼び出し先の関数(myScaler)の無名関数で仮引数(n, m)を受け取っているところになります。

自作関数の場合

const data = [
 {x: 0, y: 0},
 {x: 1, y: 1},
 {x: 2, y: 4},
 {x: 3, y: 9},
 {x: 4, y: 16},
 {x: 5, y: 25},
 {x: 6, y: 36},
 {x: 7, y: 49},
 {x: 8, y: 64},
 {x: 9, y: 81},
 {x: 10, y: 100},
];

function myScaler() {
 // 無名関数で仮引数を受け取っている n,m
 return (n, m) => {
   console.log(`[a] n * m = ${n * m}`);
   return n * m;
 };
}

const x = myScaler();

const a = data.map((item) => {

 return x(item.x, 4);
});

console.log(a);


D3の場合

import * as d3 from 'd3';

const data = [
 {x: 0, y: 0},
 {x: 1, y: 1},
 {x: 2, y: 4},
 {x: 3, y: 9},
 {x: 4, y: 16},
 {x: 5, y: 25},
 {x: 6, y: 36},
 {x: 7, y: 49},
 {x: 8, y: 64},
 {x: 9, y: 81},
 {x: 10, y: 100},
];

const d3Scaler = d3.scaleLinear().domain([0, 10]).range([0, 40]);

const b = data.map((item) => {
 return d3Scaler(item.x);
});

console.log(b);


上記の自作関数を少し変えて以下を実行してみます。

const data = [
  { x: 0, y: 0 },
  { x: 1, y: 1 },
  { x: 2, y: 4 },
  { x: 3, y: 9 },
  { x: 4, y: 16 },
  { x: 5, y: 25 },
  { x: 6, y: 36 },
  { x: 7, y: 49 },
  { x: 8, y: 64 },
  { x: 9, y: 81 },
  { x: 10, y: 100 },
];

function scale(domain, range) {
  const m = (range[1] - range[0]) / (domain[1] - domain[0]);
  return (num) => range[0] + m * (num - domain[0]);
}

const f = scale([0, 10], [0, 40]);

const a = data
  .map((n, index) => {
    return n.x;
  })
  .map((n, index) => {
    return f(n);
  });

console.log(a);


実行結果です。

$ time node -r esm index.js
[
   0,  4,  8, 12, 16,
  20, 24, 28, 32, 36,
  40
]

real    0m0.237s
user    0m0.210s
sys     0m0.029s


D3ライブラリは無名関数ないしはコールバック関数が多くの場面で出てくるので、利用シーンごとに関数代入と実引数と仮引数、そして無名関数などの役割を想像できるかどうかは理解の進み具合に影響を与えるようなイメージです。


というのも、バージョンが古いサンプルだと現在の最新ライブラリを使って書き換えみたいなことをすると、コールバックで受け取る仮引数の順序が違って、意図しない動きになったりというのが、あったりします。例えば


古いバージョンのサンプルで以下のようになっていたので、

(_event, _data)=>{console.log(`[data]:${_data}, [event]:${_event}`)}

そのままログに出そうと思ったけど、dataに来てほしいものは実は最新バージョンだとeventだったみたいなケースがあります。

(_data, _event)=>{console.log(`[data]:${_data}, [event]:${_event}`)}


こういうのを一度経験しておくと、2回目以降は割とスムーズにリファクタリングしていけるのではないかなーなんて考えています。


簡単ですが、以上になります。

この記事が気に入ったらサポートをしてみませんか?