2

I need some custom way to resolve variable which might have transitive and verify cyclic/un-resolvable fields. e.g.

var v1 = 'asd';
var v2 = '@@{v1}@@';
var v3 = '@@{v2}@@';
var v4 = '@@{v3}@@';
var v5 = '@@{v4}@@';
var v6 = '@@{v5}@@_@@{v3}@@';

var a1 = '@@{v1}@@_@@{a3}@@';
var a2 = '@@{a1}@@';
var a3 = '@@{a2}@@';

var x1 = 'asd';
var x2 = '@@{x1}@@';
var x3 = '@@{x1}@@_@@{x2}@@';

var p1 = '@@{v4}@@_@@{xyz}@@';
var p2 = '@@{xyz}@@';

const getVariableNames = str => {
    str += '';
    const variables = str.match(/(@@{[^}{)(\]\[\-+*\/]+?}@@)+?/g);
    return variables && variables.length ? variables.map(i => i.substring(3, i.length - 3)) : false;
};

const isCyclic = (myMap, node, target, [...visited] = []) => {
    if (myMap[node]) {
        if (node === target) {
            return true;
        }
        if (visited.includes(node) || !myMap[node].dep.usedIns) {
            return false;
        }
        visited.push(node);
        return myMap[node].dep.usedIns.some(n => isCyclic(myMap, n, target, visited));
    }
};

var allVars = [{ name: 'v1', val: v1 }, { name: 'v2', val: v2 },
    { name: 'v3', val: v3 }, { name: 'v4', val: v4 }, { name: 'v5', val: v5 }, { name: 'v6', val: v6 },
    { name: 'a1', val: a1 }, { name: 'a2', val: a2 }, { name: 'a3', val: a3 }, { name: 'x1', val: x1 },
    { name: 'x2', val: x2 }, { name: 'x3', val: x3 }];

function getValueMap(arr) {
    // need to get implemented
    var nodeAVL = [];
    const varNamesMap = arr.reduce((a, b) => {
        a[b.name] = { ...b, v: getVariableNames(b.val), dependsOn: [], usedIn: [], cyclic: [], unresolved: [] };
        return a;
    }, {});
    return varNamesMap;
}

var retVar = getValueMap(allVars);

console.log(retVar);
// should console
/* {
    v1: { val: 'asd', dependsOn: [], usedIn: ['v2', 'v3', 'v4', 'v5', 'v6', 'p1'], cyclic: [], unresolved: [] },
    v2: { val: 'asd', dependsOn: ['v1'], usedIn: ['v3', 'v4', 'v5', 'v6'], cyclic: [], unresolved: [] },
    v3: { val: 'asd', dependsOn: ['v2'], usedIn: ['v4', 'v5', 'v6'], cyclic: [], unresolved: [] },
    v4: { val: 'asd', dependsOn: ['v3'], usedIn: ['v5', 'v6'], cyclic: [], unresolved: [] },
    v5: { val: 'asd', dependsOn: ['v4'], usedIn: ['v6'], cyclic: [], unresolved: [] },
    v6: { val: 'asd_asd', dependsOn: ['v5', 'v3'], usedIn: [], cyclic: [], unresolved: [] },

    a1: { val: '', dependsOn: ['v1'], usedIn: [], cyclic: ['a1', 'a2', 'a3'], unresolved: [] },
    a2: { val: '', dependsOn: [], usedIn: [], cyclic: ['a1', 'a2', 'a3'], unresolved: [] },
    a3: { val: '', dependsOn: [], usedIn: [], cyclic: ['a1', 'a2', 'a3'], unresolved: [] },

    x1: { val: 'asd', dependsOn: [], usedIn: [], cyclic: [], unresolved: [] },
    x2: { val: 'asd', dependsOn: ['x1'], usedIn: ['x3'], cyclic: [], unresolved: [] },
    x3: { val: 'asd_asd', dependsOn: ['x1', 'x2'], usedIn: [], cyclic: [], unresolved: [] },

    p1: { val: '', dependsOn: ['v4'], usedIn: [], cyclic: [], unresolved: ['xyz'] },
    p2: { val: '', dependsOn: [], usedIn: [], cyclic: [], unresolved: ['xyz'] },
};*/

My expected output is

{
    v1: { val: 'asd', dependsOn: [], usedIn: ['v2', 'v3', 'v4', 'v5', 'v6', 'p1'], cyclic: [], unresolved: [] },
    v2: { val: 'asd', dependsOn: ['v1'], usedIn: ['v3', 'v4', 'v5', 'v6'], cyclic: [], unresolved: [] },
    v3: { val: 'asd', dependsOn: ['v2'], usedIn: ['v4', 'v5', 'v6'], cyclic: [], unresolved: [] },
    v4: { val: 'asd', dependsOn: ['v3'], usedIn: ['v5', 'v6'], cyclic: [], unresolved: [] },
    v5: { val: 'asd', dependsOn: ['v4'], usedIn: ['v6'], cyclic: [], unresolved: [] },
    v6: { val: 'asd_asd', dependsOn: ['v5', 'v3'], usedIn: [], cyclic: [], unresolved: [] },

    a1: { val: '', dependsOn: ['v1'], usedIn: [], cyclic: ['a1', 'a2', 'a3'], unresolved: [] },
    a2: { val: '', dependsOn: [], usedIn: [], cyclic: ['a1', 'a2', 'a3'], unresolved: [] },
    a3: { val: '', dependsOn: [], usedIn: [], cyclic: ['a1', 'a2', 'a3'], unresolved: [] },

    x1: { val: 'asd', dependsOn: [], usedIn: [], cyclic: [], unresolved: [] },
    x2: { val: 'asd', dependsOn: ['x1'], usedIn: ['x3'], cyclic: [], unresolved: [] },
    x3: { val: 'asd_asd', dependsOn: ['x1', 'x2'], usedIn: [], cyclic: [], unresolved: [] },

    p1: { val: '', dependsOn: ['v4'], usedIn: [], cyclic: [], unresolved: ['xyz'] },
    p2: { val: '', dependsOn: [], usedIn: [], cyclic: [], unresolved: ['xyz'] },
}
Akhilesh Kumar
  • 9,085
  • 13
  • 57
  • 95
  • 2
    This is not clear. Eg "might have" & "verify" are vague. Please in code questions give a [mre]--cut & paste & runnable code; example input (as initialization code) with desired & actual output (including verbatim error messages); tags & versions; clear specification & explanation. That includes the least code you can give that is code that you show is OK extended by code that you show is not OK. (Debugging fundamental.) Lack of one is reason to close, but unfortunately we can't while the bounty is on. – philipxy Oct 27 '19 at 03:35
  • Thanks @philipxy but I think I misguided you basically here "might have" & "verify" word are not used to make it vague but if required case is not matched then proposed solution can have algorithm to make more performant e.g. in our case if set of variables does not have "transitive/cyclic/un-resolvable" fields the solution need not to perform complex task (only if algorithm of solution supports it). Edit suggestion is welcome if you feel so. It might help me in future. Thanks again – Akhilesh Kumar Oct 28 '19 at 06:48
  • 2
    Please clarify via edits, not comments. Although your comment is not clear & does not clarify your question or address my earlier comment. Please act on it. Code questions without a [mre] should be closed & downvoted until adequately improved (& commented on for clarification). Don't expect us to divine what you are not giving. PS What I mean by vague is that those are generic terms but neither they nor you explain the details that motivated them. – philipxy Oct 30 '19 at 00:46

1 Answers1

1

This almost outputs your expected output:

const v1 = 'asd';
const v2 = '@@{v1}@@';
const v3 = '@@{v2}@@';
const v4 = '@@{v3}@@';
const v5 = '@@{v4}@@';
const v6 = '@@{v5}@@_@@{v3}@@';

const a1 = '@@{v1}@@_@@{a3}@@';
const a2 = '@@{a1}@@';
const a3 = '@@{a2}@@';

const x1 = 'asd';
const x2 = '@@{x1}@@';
const x3 = '@@{x1}@@_@@{x2}@@';

const p1 = '@@{v4}@@_@@{xyz}@@';
const p2 = '@@{xyz}@@';

const getVariableNames = (str) => {
    str += '';
    const variables = str.match(/(@@{[^}{)(\][\-+*/]+?}@@)+?/g);
    return variables && variables.length ? variables.map(i => i.substring(3, i.length - 3)) : false;
};

const getVal = (str, arr) => arr.filter(v => v.name === str)[0].val;

const isCyclic = (varName, arr, visited = [varName]) => {
    const varNames = getVariableNames(getVal(varName, arr));
    if (!varNames) return false;
    if (varNames.some(n => getVariableNames(getVal(n, arr)) && getVariableNames(getVal(n, arr)).some(v => visited.includes(v)))) return true;
    return varNames.some(n => isCyclic(n, arr, visited));
};

const isUnresolved = (varName, arr) => !arr.filter(v => v.name === varName).length;

const resolve = (str, arr) => {
    const varNames = getVariableNames(str);
    if (varNames) varNames.forEach(n => str = str.replace(`@@{${n}}@@`, resolve(getVal(n, arr), arr)));
    return str;
};

const usedIn = (varName, map, arr) => {
    const result = [];
    Object.entries(map)
        .filter(([, value]) => value.dependsOn.includes(varName))
        .forEach(([name, value]) => {
            const varNames = getVariableNames(value.val);
            if (varNames && !value.unresolved.length) result.push(...usedIn(name, map, arr));
            else result.push(name);
        });
    return result;
};

const getValueMap = (arr) => {
    const varNamesMap = arr.reduce((acc, curr) => {
        let val = '';
        const dependsOn = [];
        const cyclic = [];
        const unresolved = [];

        const varNames = getVariableNames(curr.val);
        if (varNames) {
            varNames.forEach(varName => {
                if (isUnresolved(varName, arr) || isCyclic(varName, arr)) {
                    if (isUnresolved(varName, arr)) unresolved.push(varName);
                    else if (isCyclic(varName, arr)) cyclic.push(varName);
                    val = '';
                } else dependsOn.push(varName);
            });
        }

        if (!cyclic.length && !unresolved.length) val = resolve(curr.val, arr);

        acc[curr.name] = { val, dependsOn, usedIn: [], cyclic, unresolved };
        return acc;
    }, {});

    Object.keys(varNamesMap).forEach(name => varNamesMap[name].usedIn.push(...usedIn(name, varNamesMap, arr)));

    return varNamesMap;
};

const allVars = [
    { name: 'v1', val: v1 }, { name: 'v2', val: v2 }, { name: 'v3', val: v3 },
    { name: 'v4', val: v4 }, { name: 'v5', val: v5 }, { name: 'v6', val: v6 },
    { name: 'a1', val: a1 }, { name: 'a2', val: a2 }, { name: 'a3', val: a3 },
    { name: 'x1', val: x1 }, { name: 'x2', val: x2 }, { name: 'x3', val: x3 },
    { name: 'p1', val: p1 }, { name: 'p2', val: p2 }
];

console.log(getValueMap(allVars));

This outputs:

{
  v1: { val: 'asd', dependsOn: [], usedIn: ['v2', 'a1'], cyclic: [], unresolved: [] },
  v2: { val: 'asd', dependsOn: ['v1'], usedIn: ['v3'], cyclic: [], unresolved: [] },
  v3: { val: 'asd', dependsOn: ['v2'], usedIn: ['v4', 'v6'], cyclic: [], unresolved: [] },
  v4: { val: 'asd', dependsOn: ['v3'], usedIn: ['v5', 'p1'], cyclic: [], unresolved: [] },
  v5: { val: 'asd', dependsOn: ['v4'], usedIn: ['v6'], cyclic: [], unresolved: [] },
  v6: { val: 'asd_asd', dependsOn: ['v5', 'v3'], usedIn: [], cyclic: [], unresolved: [] },

  a1: { val: '', dependsOn: ['v1'], usedIn: [], cyclic: ['a3'], unresolved: [] },
  a2: { val: '', dependsOn: [], usedIn: [], cyclic: ['a1'], unresolved: [] },
  a3: { val: '', dependsOn: [], usedIn: [], cyclic: ['a2'], unresolved: [] },

  x1: { val: 'asd', dependsOn: [], usedIn: ['x2', 'x3'], cyclic: [], unresolved: [] },
  x2: { val: 'asd', dependsOn: ['x1'], usedIn: ['x3'], cyclic: [], unresolved: [] },
  x3: { val: 'asd_asd', dependsOn: ['x1', 'x2'], usedIn: [], cyclic: [], unresolved: [] },

  p1: { val: '', dependsOn: ['v4'], usedIn: [], cyclic: [], unresolved: ['xyz'] },
  p2: { val: '', dependsOn: [], usedIn: [], cyclic: [], unresolved: ['xyz'] }
}

The only differences are:

  • usedIn only has the variables that use the variable directly (e.g. v3.usedIn is [v4, v6] not [v4, v5, v6])
  • cyclic only has the cyclic variables that are directly used (e.g. a2.cyclic is [a1] not [a1, a2, a3])
Lauren Yim
  • 12,700
  • 2
  • 32
  • 59