如何从JavaScript对象中具有关系的数字计算新值

How to calculate new values from numbers with relations in JavaScript object

本文关键字:有关系 数字 计算 新值 JavaScript 对象      更新时间:2023-09-26

我有以下数据:

[
    { "names" : [ "a3","printed","black" ], "value" : 15 },
    { "names" : [ "a3","copied","black" ], "value" : 87 },
    { "names" : [ "a3","printed","color" ], "value" : 37 },
    { "names" : [ "a3","printed","color","full" ], "value" : 37 },
    { "names" : [ "a3","copied","color","single" ], "value" : 0 },
    { "names" : [ "a3","copied","color","full" ], "value" : 44 },
    { "names" : [ "a3","scanned" ], "value" : 288 },
    { "names" : [ "total" ], "value" : 242142 },
    { "names" : [ "scanned" ], "value" : 67411 },
    { "names" : [ "copied","black" ], "value" : 79997 },
    { "names" : [ "copied","full","color" ], "value" : 809 },
    { "names" : [ "copied","single","color" ], "value" : 0 },
    { "names" : [ "printed","two","color" ], "value" : 0 },
    { "names" : [ "printed","black" ], "value" : 120665 },
    { "names" : [ "printed","full","color" ], "value" : 40657 }
]

我以以下方式组织了这些值之间的关系:

{    
    "colors" : { "black" : "", "color" : [ "full", "two", "single" ] },
    "functions" : { "scanned" : "", "total" : [ "printed", "copied", "faxed" ] },
    "papers" : { "a3" : "" }
}

使用这个脚本,我几乎得到了我想要的所有值:

function calculateValues(o) {
    return Object.keys(o).reduce(function (r, k) {
        var v;
        if (k === 'value') {
            return r;
        }
        v = calculateValues(o[k]);
        if (o[k].value === null) {
            o[k].value = v;
        }
        values[k] = (values[k] || 0) + o[k].value;
        return r + o[k].value;
    }, 0);
}
var data = [{ names: ["a3", "printed", "black"], value: 15 }, { names: ["a3", "copied", "black"], value: 87 }, { names: ["a3", "printed", "color"], value: 37 }, { names: ["a3", "printed", "color", "full"], value: 37 }, { names: ["a3", "copied", "color", "single"], value: 0 }, { names: ["a3", "copied", "color", "full"], value: 44 }, { names: ["a3", "scanned"], value: 288 }, { names: ["total"], value: 242142 }, { names: ["scanned"], value: 67411 }, { names: ["copied", "black"], value: 79997 }, { names: ["copied", "full", "color"], value: 809 }, { names: ["copied", "single", "color"], value: 0 }, { names: ["printed", "two", "color"], value: 0 }, { names: ["printed", "black"], value: 120665 }, { names: ["printed", "full", "color"], value: 40657 }],
    relations = { colors: { "black": "", color: ["full", "two", "single"] }, functions: { scanned: "", total: ["printed", "copied", "faxed"] }, papers: { "a3": "" } },
    priorities = ['functions', 'colors', 'papers'], // as long as keys of objects are not ordered
    order = {},
    result = {},
    values = {},
    i = 0;
priorities.forEach(function (p) {
    Object.keys(relations[p]).forEach(function (k) {
        order[k] = ++i;
        Array.isArray(relations[p][k]) && relations[p][k].forEach(function (a) {
            order[a] = ++i;
        });
    });
});
data.forEach(function (a) {
    var names = a.names.slice();
    names.sort(function (a, b) {
        return (order[a] || 0) - (order[b] || 0);
    });
    if (relations.functions.total.indexOf(names[0]) !== -1) {
        names.unshift('total');
    }
    names.reduce(function (o, k) {
        return o[k] = o[k] || { value: null };
    }, result).value = a.value;
});
calculateValues(result.total);
// calculateCount(result.scanned); 
console.log(values);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

但我错过了一些东西。例如:

a3打印黑色是15

a3打印颜色为37

什么是a3打印?(52(

另一个是:

a3复制的全彩色是:44

a3复制的彩色单曲是:0

什么是a3复制的颜色?(44(

另一个是:

a3打印颜色为:37

a3复制的颜色为:44(基于我们在上面找到的值示例(

什么是a3颜色?81

你可能应该明白我的意思是这里缺少什么。

您可以在result对象中搜索给定的指示符并迭代所有属性。然后只计算匹配的值。

function getValue(names) {
    function iter(o, p) {
        Object.keys(o).forEach(function (k) {
            var q = p.slice();
            k === q[0] && q.shift();
            if (q.length) {
                o[k] !== null && typeof o[k] === 'object' && iter(o[k], q);
                return;
            }
            v += o[k].value || 0;
        });
    }
    var v = 0;
    names.sort(sortByOrder);
    iter(result, names);
    return v;
}
function calculateValues(o) {
    return Object.keys(o).reduce(function (r, k) {
        var v;
        if (k === 'value') {
            return r;
        }
        v = calculateValues(o[k]);
        if (o[k].value === null) {
            o[k].value = v;
        }
        values[k] = (values[k] || 0) + o[k].value;
        return r + o[k].value;
    }, 0);
}
function sortByOrder(a, b) {
    return (order[a] || 0) - (order[b] || 0);
}
function getSums(a) {
    console.log(a.join(' '), getValue(a));
}
var data = [{ names: ["a3", "printed", "black"], value: 15 }, { names: ["a3", "copied", "black"], value: 87 }, { names: ["a3", "printed", "color", "full"], value: 37 }, { names: ["a3", "copied", "color", "single"], value: 0 }, { names: ["a3", "copied", "color", "full"], value: 44 }, { names: ["a3", "scanned"], value: 288 }, { names: ["total"], value: 242142 }, { names: ["scanned"], value: 67411 }, { names: ["copied", "black"], value: 79997 }, { names: ["copied", "full", "color"], value: 809 }, { names: ["copied", "single", "color"], value: 0 }, { names: ["printed", "two", "color"], value: 0 }, { names: ["printed", "black"], value: 120665 }, { names: ["printed", "full", "color"], value: 40657 }],
    relations = { colors: { "black": "", color: ["full", "two", "single"] }, functions: { scanned: "", total: ["printed", "copied", "faxed"] }, papers: { "a3": "" } },
    priorities = ['functions', 'colors', 'papers'], // as long as keys of objects are not ordered
    order = {},
    result = {},
    values = {},
    i = 0;
priorities.forEach(function (p) {
    Object.keys(relations[p]).forEach(function (k) {
        order[k] = ++i;
        Array.isArray(relations[p][k]) && relations[p][k].forEach(function (a) {
            order[a] = ++i;
        });
    });
});
data.forEach(function (a) {
    var names = a.names.slice();
    names.sort(sortByOrder);
    if (relations.functions.total.indexOf(names[0]) !== -1) {
        names.unshift('total');
    }
    names.reduce(function (o, k) {
        return o[k] = o[k] || { value: null };
    }, result).value = a.value;
});
calculateValues(result.total);
// calculateCount(result.scanned);
getSums(['a3', 'color']);
getSums(['a3', 'black']);
getSums(['a3', 'printed', 'two', 'color']);
getSums(['a3', 'printed', 'full', 'color']);
// the wanted use cases
getSums(['a3', 'printed', 'black']);          // 15
getSums(['a3', 'printed', 'color']);          // 37
getSums(['a3', 'printed']);                   // 52
getSums(['a3', 'copied', 'color', 'full']);   // 44
getSums(['a3', 'copied', 'color', 'single']); //  0
getSums(['a3', 'copied', 'color']);           // 44
getSums(['a3', 'printed', 'color']);          // 37
getSums(['a3', 'copied', 'color']);           // 44
getSums(['a3', 'color']);                     // 81
console.log(values);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

我还没有考虑过你的另一个问题,但对我来说,似乎只需要求和,你就可以使用一个简单得多的函数。

  • 使用filter在其names数组中选择具有所有所需值的条目
  • 使用reduce添加其value属性

function getEntriesWithValues(entries, values) {
  return entries
    .filter(e => values.every(v => e.names.includes(v)));
};
  
function sumValue(v1, v2) { return v1 + v2.value; }
var data = [
    { "names" : [ "a3","printed","black" ], "value" : 15 },
    { "names" : [ "a3","copied","black" ], "value" : 87 },
    { "names" : [ "a3","printed","color","full" ], "value" : 37 },
    { "names" : [ "a3","copied","color","single" ], "value" : 0 },
    { "names" : [ "a3","copied","color","full" ], "value" : 44 },
    { "names" : [ "a3","scanned" ], "value" : 288 },
    { "names" : [ "total" ], "value" : 242142 },
    { "names" : [ "scanned" ], "value" : 67411 },
    { "names" : [ "copied","black" ], "value" : 79997 },
    { "names" : [ "copied","full","color" ], "value" : 809 },
    { "names" : [ "copied","single","color" ], "value" : 0 },
    { "names" : [ "printed","two","color" ], "value" : 0 },
    { "names" : [ "printed","black" ], "value" : 120665 },
    { "names" : [ "printed","full","color" ], "value" : 40657 }
];
function getSumFromData(values) {
  return getEntriesWithValues(data, values).reduce(sumValue, 0);
};
var entriesWithA3Black = getEntriesWithValues(data, ["a3", "black"]);
var sumForA3Black = entriesWithA3Black.reduce(sumValue, 0);
console.log("Sum for A3 + black: " + sumForA3Black);
console.log("Sum for A3 + color: " + getSumFromData(["a3", "color"]));