如何外连接两个数据集(基于多个主键)?

How to outer join two data sets (based on multiple primary keys)?

假设我有以下两个数据集:

   var revenueTestData = [
{"YEAR": "2007", "MONTH": "1", "CUSTOMER": "Customer1", "REVENUE": "1938.49488391425"},
{"YEAR": "2007", "MONTH": "1", "CUSTOMER": "Customer2", "REVENUE": "75.9142774343491"},
{"YEAR": "2007", "MONTH": "2", "CUSTOMER": "Customer2", "REVENUE": "99.3456067931875"}];

    var costTestData = [
{"YEAR": "2009", "MONTH": "1", "CUSTOMER": "Customer4", "COST": "14425"},
{"YEAR": "2009", "MONTH": "1", "CUSTOMER": "Customer4", "COST": "7591"},
{"YEAR": "2009", "MONTH": "2", "CUSTOMER": "Customer5", "COST": "31875"}];

我如何(在 sql 术语中)完全外部连接两个数据集?更重要的是,我可以基于多个 columns/primary 键来完成吗?例如,在这种情况下,按 YEAR 和 CUSTOMER 加入并获取 YEAR、CUSTOMER、REVENUE 的所有值,即使年份不匹配(在这种情况下,缺失的列将为空)。

我遇到了以下编写精美的函数,我可以使用它进行 LEFT JOIN 并决定将哪些列包含在我的结果集中,但是当年份不匹配时,它们就会从结果集中消失(正如在内部联接中所预期的那样):

function innerjoinData(primary, foreign, primaryKey, foreignKey, select) {
    var m = primary.length, n = foreign.length, index = [], c = [];

    for (var i = 0; i < m; i++) {     // loop through m items
        var row = primary[i];
        index[row[primaryKey]] = row; // create an index for primary table
    }

    for (var j = 0; j < n; j++) {     // loop through n items
        var y = foreign[j];
        var x = index[y[foreignKey]]; // get corresponding row from primary
        c.push(select(x, y));         // select only the columns you need
    }

    return c;
}

调用示例如下所示:

var testChartData= innerjoinData(revenueTestData, costTestData, "YEAR", "YEAR", function (a, b) {
                return {
                    Year: b.YEAR,
                    Cost: a.COST,
                    Revenue: b.REVENUE
                };
            });

也许有人可以帮我把它变成一个外部连接?

这是对单个键进行完全连接的代码。您必须从两侧扫描它,您显示的代码是在进行左连接,而不是内部连接。支持双键应该不会太难,无限键会多花点钱。

function innerjoinData(primaryTable, foreignTable, primaryKey, foreignKey, selectColumns) {
    var primaryIndex = mapFromArray(primaryTable, primaryKey),
        foreignIndex = mapFromArray(foreignTable, foreignKey),
        resultSet = [];


    // Look for misses and matches from the left
    for (var i = 0; i < primaryTable.length; i++) {
        var primaryRow = primaryTable[i];
        var match = foreignIndex[primaryRow[primaryKey]];
        resultSet.push(selectColumns(primaryRow,  match || {}));
        
    }
    
    // Look for just misses from the right
    for (var i = 0; i < foreignTable.length; i++) {
        var foreignRow = foreignTable[i];
        if (!primaryIndex.hasOwnProperty( foreignRow[foreignKey] )) {
            resultSet.push(selectColumns({}, foreignRow))
        }        
    }
    

    return resultSet;

    function mapFromArray(list, keyByProp) {
        var map = {};
        for (var i = 0, item; item = list[i]; i++) {
            map[item[keyByProp]] = item;
        }
        return map;
    };
}

var revenueTestData = [{
    "YEAR": "2006",
        "MONTH": "1",
        "CUSTOMER": "Customer1",
        "REVENUE": "1938.49488391425"
}, {
    "YEAR": "2007",
        "MONTH": "1",
        "CUSTOMER": "Customer2",
        "REVENUE": "75.9142774343491"
}, {
    "YEAR": "2008",
        "MONTH": "2",
        "CUSTOMER": "Customer2",
        "REVENUE": "99.3456067931875"
}];


var costTestData = [{
    "YEAR": "2007",
        "MONTH": "1",
        "CUSTOMER": "Customer4",
        "COST": "14425"
}, {
    "YEAR": "2008",
        "MONTH": "1",
        "CUSTOMER": "Customer4",
        "COST": "7591"
}, {
    "YEAR": "2009",
        "MONTH": "2",
        "CUSTOMER": "Customer5",
        "COST": "31875"
}];


var testChartData = innerjoinData(costTestData, revenueTestData, "YEAR", "YEAR", function (primaryRow, foreignRow) {
    return {
        Year: foreignRow.YEAR || primaryRow.YEAR,
        Cost: primaryRow.COST,
        Revenue: foreignRow.REVENUE,
        Customer:foreignRow.CUSTOMER
    };
});

document.getElementById('pre').innerHTML = JSON.stringify(testChartData, null, 4)
<pre id="pre"></pre>