0

I'm trying to apply this function How parameterize array to as single comma separated value? in a 'function called by its name' script but it doesn't work as expected:

function add(a,b,c){
    return a+b+c
}

var vals = callFct("add", window, 2,3,4); 
document.getElementById('result').innerHTML += "<br>Function result: "+vals
// result = Function result: 9 ----> 2+3+4 : OK

The values are coming from: json={a:[2,3,4]}

function serialize(vals){
    var output = [];
    for (var prop in vals) {
        if (vals.hasOwnProperty(prop)){
            output.push(vals[prop].join(','));
        }
    }
    return output;
}

var json = {a:[2,3,4]};
document.getElementById('result').innerHTML += "<br>Used data:  "+serialize(json)
// result = Used data: 2,3,4 ---> seems to be OK... but

var test = callFct("add", window, serialize(json)); 
document.getElementById('result').innerHTML += "<br>Function result:  "+test
// result = Function result : 2,3,4undefinedundefined ---> oups

I've tried to remove the 'undefined' ...

document.getElementById('result').innerHTML += "<br>Function result :  "+test.replace(/undefined/g,'')
// result = Function result : 2,3,4 ---> nope

The called function:

function callFct(w, y, z ) {  
  var z = Array.prototype.slice.call(arguments, 2);
  console.log('vars = '+z)
  let v = w.split(".");
  let x = v.pop();
  for(var i = 0; i < v.length; i++) {
    y = y[v[i]];
  }
  return y[x].apply(y, z);
}

What's wrong in my way to proceed?

Wolden
  • 195
  • 1
  • 12

2 Answers2

1

Please provide better parameter names than w, y, and z...

If you change the names to the one's seen in the demo below, your return statement will look like the following:

return scope[last].apply(funcName, serialized);

If you notice, you are calling apply on window.add. Make sure that method exists. I am not sure what add looks like, but it will take the "serialized" JSON data and the scope of the call will be the incoming string (first) parameter (which can apparently be dot-separated).

Notes

  1. You can change this: [...output, json[prop].join(',')] to this: output.concat(json[prop].join(',')) for browser compatability.
  2. A "basic" polyfill for flatMap can be found here: https://stackoverflow.com/a/39838385/1762224

const json = { a: [2, 3, 4] };

document.getElementById('result-1').innerHTML = 'Values: ' + serialize(json);

let sum = callFct('add', window, serialize(json));
document.getElementById('result-2').innerHTML = 'Sum: ' + sum;

let difference = callFct('subtract', window, serialize(json));
document.getElementById('result-3').innerHTML = 'Difference: ' + difference;

function serialize(json) {
  return Object.keys(json).reduce((output, prop) => {
    return [...output, json[prop].join(',')];
  }, []);
}

function callFct(funcName, scope, serialized) {
  serialized = Array.from(arguments).slice(2);
  console.log('vars = ' + serialized)
  const tokens = funcName.split('.');
  const last = tokens.pop();
  for (let i = 0; i < tokens.length; i++) {
    scope = scope[tokens[i]];
  }
  return scope[last].apply(funcName, serialized);
}

function prepareValues(serialized) {
  return serialized
    .flatMap(v => v.split(/,\s*/g))
    .map(v => parseInt(v, 10));
}

function add(serialized) {
  return prepareValues(serialized)
    .reduce((s, v) => s + v, 0);
}

function subtract(serialized) {
  return prepareValues(serialized)
    .reduce((s, v) => s - v);
}
div[id^="result-"] { font-family: monospace; }
<script src="https://polyfill.io/v3/polyfill.min.js?features=Array.prototype.flatMap"></script>
<div id="result-1"></div>
<div id="result-2"></div>
<div id="result-3"></div>
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
  • it doesn't solve the prob: used data produced as explained in the question are right (2,3,4) but not in an exploitable format by function. – Wolden Jun 30 '20 at 13:19
  • @Wolden OK, with your included function, that you provided after I originally answered, I tried to explain what is happening. – Mr. Polywhirl Jun 30 '20 at 14:09
  • @Wolden I updated my `add` function to flat-map the results and then split the values. Your serialize is returning an array of "serialized" values. It is not a true string. You are building the results as an array of the joined values for each key. – Mr. Polywhirl Jun 30 '20 at 15:03
  • yeeeeeeees thanks so much, it works perfectly :-) ...btw, is there a "classical way" to replace flatMap which is not yet recognized by all browsers? – Wolden Jun 30 '20 at 15:10
  • @Wolden you can create a polyfill. I will add it to the question. – Mr. Polywhirl Jun 30 '20 at 15:34
0

Finally found a simpliest solution:

// define empty vars
let a, b, c;    
const vars = [a, b, c];

// call function by its name
function callFct(fct) {  
  const toks = fct.split(".");
  const last = toks.pop();
  for(var i = 0; i < toks.length; i++) {
    window = window[toks[i]];
  }
  return window[last].apply(window, vars); // vars now in array
}

// functions to be called
function add(){
   return a+b+c
}
function substract(){
   return a-b-c
}  
function multiply(){
   return a*b*c
}

// produce real vars values
a = 20, b = 10, c = 5;

// get results
var add = callFct("add");
console.log(add); // 20+10+5 = 35

c = -5 // actualize any value
var subs = callFct("substract");
console.log(subs); // 20-10--5 = 15

var mult = callFct("multiply");
console.log(mult); // -1000

// mixing functions results
function mix(){
    return add/subs
}
var mixed = callFct("mix");
console.log(mixed); // 2.3333333333333335
Wolden
  • 195
  • 1
  • 12