使用jQuery合并JSON数组

5

我已经花了整个早上在这上面摆弄并阅读,但发现自己陷入了死循环!

我正在尝试使用出色的AmCharts Javascript Charts绘制图表,以显示我的股票持有量和股票转换率。

我无法从一个查询中获取两组数据,并且不能使用AmCharts StockChart,因为它不是基于时间的数据...因此,我有两组需要使用Javascript合并的数据。

数据被从数据库中提取并成功返回为类似于此的JSON数组:

销售数据:

[{"brandName":"Fender","gearShiftedPerMonth":"35","retailSalesPerMonth":"55"},
 {"brandName":"Gibson","gearShiftedPerMonth":"23","retailSalesPerMonth":"43"},
 {"brandName":"Epiphone","gearShiftedPerMonth":"10","retailSalesPerMonth":"13"}]

股票数据:

[{"brandName":"Gibson","stockValue":"1234"},
 {"brandName":"Fender","stockValue":"975"},
 {"brandName":"Epiphone","stockValue":"834"}]

显然,该示例中的实际数字是虚构的!
现在,我需要将它们组合起来创建以下内容: 组合数据
[{"brandName":"Fender","gearShiftedPerMonth":"35","retailSalesPerMonth":"55","stockValue":"975"},
 {"brandName":"Gibson","gearShiftedPerMonth":"23","retailSalesPerMonth":"43","stockValue":"1234"},
 {"brandName":"Epiphone","gearShiftedPerMonth":"10","retailSalesPerMonth":"13","stockValue":"834"}]

我们所拥有的是销售数据集和库存数据集的合并,以添加相应品牌名称记录的stockValue附加数据。
我尝试使用$.extend,但无法在这种情况下使用它。
值得注意的是,数据对可能不一定按正确顺序排列,并且可能存在不匹配的情况,因此必须实施某种零误差捕捉。

为什么不能从一个查询中获取两组数据? - Waleed Khan
4个回答

4
首先,您需要将数据转换为两个对象,这两个对象的属性是您想要合并在一起的值:
{
"Fender" : {"gearShiftedPerMonth":"35","retailSalesPerMonth":"55"},
"Gibson" : {"gearShiftedPerMonth":"23","retailSalesPerMonth":"43"},
"Epiphone" : {"gearShiftedPerMonth":"10","retailSalesPerMonth":"13"}
}

并且

{
"Gibson": {"stockValue":"1234"},
"Fender": { "stockValue":"975"},
"Epiphone": { "stockValue":"834"}
}

一旦转换完成,您将得到两个对象,可以使用$.extend或其他函数进行合并。

更新

对于大型数据集,这几乎可以在线性时间内获得结果:

var salesa = {}, stocka = {};
$.each(sales, function(i, e) {
    salesa[e.brandName] = e;
});
$.each(stock, function(i, e) {
    stocka[e.brandName] = e;
});

var combine = {};
$.extend(true, combine, salesa, stocka)

如果在第二个转换回调函数中进行合并(使用$each(stock...)而不是单独调用$.extend()),可以进一步优化速度,但这样会失去一些明显性。


不需要将数据从数组中转换出来。 - jholloman
@jholloman:转换数据将确保品牌名称之间的数据匹配。请注意,在问题中,品牌名称与数组索引不一致。+1 - user1106925
抱歉,我错误地假设扩展解决方案是正确的。您认为将其转换为对象的最有效方法是什么? - jholloman
@jholloman 没问题。我更新了答案,提供了一个几乎以线性时间运行的示例,不像其他答案需要双重嵌套循环。这使用更多的空间,但是运行的指令要少得多。它可以按照所示进行更紧密的处理,或者如果我放弃jQuery调用并使用纯JavaScript。 - Clinton Pierce

2
如果您的示例代码反映了现实情况,那么jQuery的$.extend将不是这个问题的正确解决工具。它盲目地从一个对象复制数据到另一个对象。请注意,您的数据顺序不一致。销售数据中首先是Fender,而库存数据中首先是gibson
因此,jQuery的$.extend正在混合这两个结果。对于Fender的"gearShifted"和"retailSales"最终与Gibson的"brandName"和"stockValue"相结合。
你需要做的是迭代一个数组,并在另一个数组中查找"brandName",然后复制你想要的数据。如果您愿意,可以使用$.extend来完成这部分内容...
var sales_data = 
[{"brandName":"Fender","gearShiftedPerMonth":"35","retailSalesPerMonth":"55"},
 {"brandName":"Gibson","gearShiftedPerMonth":"23","retailSalesPerMonth":"43"},
 {"brandName":"Epiphone","gearShiftedPerMonth":"10","retailSalesPerMonth":"13"}]

var stock_data = 
[{"brandName":"Gibson","stockValue":"1234"},
 {"brandName":"Fender","stockValue":"975"},
 {"brandName":"Epiphone","stockValue":"834"}]

var combined = $.map(sales_data, function(obj, i) {

    return $.extend({}, obj, $.grep(stock_data, function(stock_obj) {
        return obj.brandName === stock_obj.brandName
    })[0]);
});

请注意这种方法并不是非常高效,但除非数据集非常庞大,否则应该不会有问题。


演示: http://jsfiddle.net/sDyKx/

结果:

[
    {
        "brandName": "Fender",
        "gearShiftedPerMonth": "35",
        "retailSalesPerMonth": "55",
        "stockValue": "975"
    },
    {
        "brandName": "Gibson",
        "gearShiftedPerMonth": "23",
        "retailSalesPerMonth": "43",
        "stockValue": "1234"
    },
    {
        "brandName": "Epiphone",
        "gearShiftedPerMonth": "10",
        "retailSalesPerMonth": "13",
        "stockValue": "834"
    }
]

我本以为自己错了,结果被踩了并删除了我的回答以减少混乱。我应该坚持自己的观点。 - Clinton Pierce
@clintp:恢复你的答案,我会给你+1。如果数据可以转换,你的解决方案也是另一个好方法。 - user1106925
如果您知道存在1:1的关系,那么按brandName对数组进行排序,然后从那里合并可能会更快。这里的解决方案在最坏情况下都是O(N*M),而排序可能只有O(log N * log M)。这就是为什么我一开始采取了变换方法的原因。 - Clinton Pierce
@clintp:是的,你说得对。如果数据可以像你展示的那样转换,那就会是一个更好的解决方案。排序可能有所帮助,但只有在需要反复访问不同数据集时才有意义。由于数据正在合并,单独的数据集有点过时。假设服务器上不能转换数据,我可能会在客户端构建你的数据结构,而不是坚持使用数组查找。 - user1106925
哦,由于某种原因,我才刚刚注意到这里所有这些额外的答案。我总是支持使用最少的代码行数。@clintp和“am not i am”你们的答案非常好。我的数据不是很大,但我仍然追求效率。你们会有什么建议?稍微离题一点,clintp能否指向一篇解释你提到的数学的文章?ONM)等等 - Jamie Hartnoll

2
我认为他想要做的是将两个数据集按照品牌名称作为关键字连接起来,就像连接表格一样。从我测试的结果来看,jQuery 的 $.extend() 函数不能处理这个问题,它只是根据它接收到的对象数组中的索引合并对象。我认为需要手动匹配键值。
stock = [{"brandName":"Fender","gearShiftedPerMonth":"35","retailSalesPerMonth":"55"},
 {"brandName":"Gibson","gearShiftedPerMonth":"23","retailSalesPerMonth":"43"},
 {"brandName":"Epiphone","gearShiftedPerMonth":"10","retailSalesPerMonth":"13"}];
value = [{"brandName":"Gibson","stockValue":"1234"},
 {"brandName":"Fender","stockValue":"975"},
 {"brandName":"Epiphone","stockValue":"834"}];

var results = [];
$(stock).each(function(){
    datum1 = this;
    $(value).each(function() {
        datum2 = this;
        if(datum1.brandName == datum2.brandName)
            results.push($.extend({}, datum1, datum2));
    });
});

这将导致:
[{"brandName":"Fender","gearShiftedPerMonth":"35","retailSalesPerMonth":"55","stockValue":"975"},
{"brandName":"Gibson","gearShiftedPerMonth":"23","retailSalesPerMonth":"43","stockValue":"1234"},
{"brandName":"Epiphone","gearShiftedPerMonth":"10","retailSalesPerMonth":"13","stockValue":"834"}]

使用$.extend()的返回值:

[{"brandName":"Gibson","gearShiftedPerMonth":"35","retailSalesPerMonth":"55","stockValue":"1234"},
{"brandName":"Fender","gearShiftedPerMonth":"23","retailSalesPerMonth":"43","stockValue":"975"},
{"brandName":"Epiphone","gearShiftedPerMonth":"10","retailSalesPerMonth":"13","stockValue":"834"}]

这是正确的。然而,看看我的下面的答案,它提供了一个稍微不同的方法,可以在处理非常大的数据集时更加高效,虽然需要牺牲一些内存。 - Clinton Pierce

1

使用原生 JavaScript 你可以这样做:

var sales = [{"brandName":"Fender","gearShiftedPerMonth":"35","retailSalesPerMonth":"55"},
 {"brandName":"Gibson","gearShiftedPerMonth":"23","retailSalesPerMonth":"43"},
 {"brandName":"Epiphone","gearShiftedPerMonth":"10","retailSalesPerMonth":"13"}];

var stock = [{"brandName":"Gibson","stockValue":"1234"},
 {"brandName":"Fender","stockValue":"975"},
 {"brandName":"Epiphone","stockValue":"834"}];

var combined = stock.slice(0);     

for (var i = 0; i < stock.length; i++) {
    for (var j = 0; j < sales.length; j++) {
        if (stock[i].brandName === sales[j].brandName) {
            for (var attrname in sales[j]) { combined[i][attrname] = sales[j][attrname]; }
        }
    }
}

JSON.stringify(combined)

生成

[
{"brandName":"Gibson","stockValue":"1234","gearShiftedPerMonth":"23","retailSalesPerMonth":"43"},
{"brandName":"Fender","stockValue":"975","gearShiftedPerMonth":"35","retailSalesPerMonth":"55"},
{"brandName":"Epiphone","stockValue":"834","gearShiftedPerMonth":"10","retailSalesPerMonth":"13"}
]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接