0

I have an object of objects:

nestedObj = {obj1:
{cat:'thing1', 
 scale: d3.scaleSequential(d3.interpolateGreens)},
 ...
}

And then I iterate over an array of things to create objects that are otherwise duplicative:

things = ['thing1','thing2',...]
// A loop that creates:
nestedObj.obj2 = Object.assign({},nestedObj.obj1,{cat:thing2}}

Then later in my code, I set properties of each function iteratively:

Object.keys(nestedObj)forEach(function(key){
 nestedObj[key].scale().domain([0,number])
}

Which results in obj1.scale being overwritten by obj2.scale, I think because the function is a shallow copy. jQuery.extend(true) and JSON.parse(JSON.stringify()) don't seem to work. Is there any way around this without changing my general order of operations?

Thank you!

Kyouma
  • 320
  • 6
  • 14
  • See the update to my answer, below. I think you have a different problem with the way you are setting and using `scale`. – terrymorse May 16 '20 at 14:02

2 Answers2

3

It's not clear that your application requires the cloning of a function, but there is a way to do that.

I've included a cloning function below.

function cloneFunction (fn) {
  let fnStr = fn.toString();
  let fnClone = new Function('return ' + fnStr);
  return fnClone();
}

let myFunc = function (a) { return a * 2; }
console.log('myFunc(1):', myFunc(1));

let mfClone = cloneFunction(myFunc);
console.log('mfClone(2):', mfClone(2));

Is this really a function cloning problem?

Is the issue truly a problem with cloning functions? Based on the information provided in the question, there seems to be a more fundamental problem.

The reported trouble item is scale:

scale: d3.scaleSequential(d3.interpolateGreens)

This expression sets the value of scale to the following function:

function scale(x) {
  return isNaN((x = +x))
    ? unknown
    : interpolator(
        k10 === 0
          ? 0.5
          : ((x = (transform(x) - t0) * k10),
            clamp ? Math.max(0, Math.min(1, x)) : x)
      );
}

Then, at some later point, scale is invoked in this statement:

nestedObj[key].scale().domain([0,number)

which includes a call to scale() with no argument. That returns undefined, so scale().domain([0,number) produces a TypeError.

Here's a code snippet that shows the problem in action:

const result = document.getElementById('result');

function emitData() {
  let scale = d3.scaleSequential(d3.interpolateGreens);
  result.innerHTML = 'scale.toString():\n' + '"' + scale.toString() +
    '"\n\n';
  result.innerHTML += 'scale() returns:\n' + scale();
  result.innerHTML += '\n\nscale().domain([0,1]) returns:\n';
  try {
    let test = scale().domain([0,1]);
    result.innerHTML += test;
  } catch (e) {
    result.innerHTML += '"' + e.toString() + '"';
  }
}
pre {
  padding: 0.5rem;
  border: 1px solid #aaa;
  width: 95%;
  min-height: 6rem;
  white-space: pre;
  overflow-x: auto;
  display: inline-block;
}
<p id="client">Testing "scale = d3.scaleSequential(d3.interpolateGreens)"</p>
<button onclick="emitData()">Run Test</button><br/>
<pre id="result"></pre>
<script src="https://d3js.org/d3-array.v2.js"></script>
<script src="https://d3js.org/d3-color.v1.js"></script>
<script src="https://d3js.org/d3-format.v1.js"></script>
<script src="https://d3js.org/d3-interpolate.v1.js"></script>
<script src="//d3js.org/d3-scale-chromatic.v0.3.js"></script>
<script src="https://d3js.org/d3-time.v1.js"></script>
<script src="https://d3js.org/d3-time-format.v2.js"></script>
<script src="https://d3js.org/d3-scale.v3.js"></script>
terrymorse
  • 6,771
  • 1
  • 21
  • 27
  • 1
    only works if there are no dependencies ... `a = (b) => b + b; c = (d) => d * a(d);` will never work – balexandre May 16 '20 at 03:21
  • @balexandre If you mean that `cloneFunction()` does a shallow copy of a function, that is true. It won't clone any function references within the target function. – terrymorse May 16 '20 at 13:29
1

you can create a deep clone of your object like below:

const obj = {a: 'a'};
const deepClone = JSON.parse(JSON.stringify(obj));

N.SH
  • 616
  • 7
  • 20