合并两个JavaScript对象,将相同属性的值相加

12
我有两个或更多的JavaScript对象。我想将它们合并,添加相同属性的值,然后按值降序排序。
例如:
var a = {en : 5,fr: 3,in: 9}
var b = {en: 8,fr: 21,br: 8}

var c = merge(a,b)

c应该像这样:

c = {
fr: 24,
en: 13,
in:9,
br:8
} 

也就是说,两个对象都被合并,相同键的值将被相加,然后再按键排序。

这是我尝试过的:

var a = {en : 5,fr: 3,in: 9}
var b = {en: 8,fr: 21,br: 8}
c = {}

// copy common values and all values of a to c
for(var k in a){
  if(typeof b[k] != 'undefined'){  
    c[k] = a[k] + b[k]  
  }
  else{ c[k] = a[k]}
}

// copy remaining values of b (which were not common)
for(var k in b){
 if(typeof c[k]== 'undefined'){
  c[k] = b[k]
 }
} 

// Create a object array for sorting
var arr = [];

for(var k in c){
 arr.push({lang:k,count:c[k]})
}

// Sort object array
arr.sort(function(a, b) {
   return b.count - a.count;
})

但是我认为这不太好。循环太多了 :( 如果有人能提供一个更简洁和好的代码就好了。


请查看此链接:http://api.jquery.com/jQuery.merge/ - mgraph
@Dogbert 到目前为止,我在这里, http://pastebin.com/VQehhcri - Jashwant
普通对象不会排序。您需要使用数组来实现排序。 - Chris Morgan
@NicolaPeluchetti,我需要以任何方式对这些数据进行排序,如果我将对象数组创建并进行排序(如我的代码中所示),是否有问题? - Jashwant
1
@Jashwant,只要属性是数字,你的排序代码就没问题。 - Nicola Peluchetti
显示剩余2条评论
4个回答

3
在ES2015+中,对象属性是有序的(首先按升序排列数字键,然后按插入顺序排列非数字键)。如果使用已指定迭代顺序的方法(如Object.getOwnPropertyNames),则规范保证了这一点。
在ES2020+中,以前未指定枚举顺序的方法现在已被指定(尽管环境已经遵循它很长时间了)。
但您必须确保没有任何属性是数字(否则,无论插入顺序如何,它们都会首先出现在非数字属性之前)。
使用reduce迭代每个对象并创建或添加到累加器上的相同属性。然后,对对象条目进行排序,并使用Object.fromEntries将其转换为具有排序属性的对象。不需要jQuery:

var a = {en : 5,fr: 3,in: 9}
var b = {en: 8,fr: 21,br: 8}
console.log(merge(a, b));

function merge(...objects) {
  const merged = objects.reduce((a, obj) => {
    Object.entries(obj).forEach(([key, val]) => {
      a[key] = (a[key] || 0) + val;
    });
    return a;
  }, {});
  return Object.fromEntries(
    Object.entries(merged).sort(
      (a, b) => b[1] - a[1]
    )
  );
}


2

无法对对象的属性进行排序,但可以对数组进行排序:

var merged = $.extend({}, a);
for (var prop in b) {
    if (merged[prop]) merged[prop] += b[prop];
    else merged[prop] = b[prop];
}
// Returning merged at this point will give you a merged object with properties summed, but not ordered.
var properties = [];
for (var prop in merged) {
    properties.push({
        name: prop,
        value: merged[prop]
    });
}
return properties.sort(function(nvp1, nvp2) {
    return nvp1.value - nvp2.value;
});

我已将 foreach 改为 for,将 name=prop 改为 name:prop, 但它仍然返回错误 :( - Jashwant
我虽然不理解代码,但这就是我所说的较为简洁的代码。这里发生了什么? 返回nvp1.value == nvp2.value?0:nvp1.value > nvp2.value?1:-1; - Jashwant
@Jashwant 我已经将那一行更新为更简单的形式:)。但是在上面的代码中,使用条件运算符 ? 来检查值是否相等,如果相等则返回 0,否则如果 value1 大于 value2,则返回 1,否则返回 -1。 - Rich O'Kelly
您期望得到一个对象,但是使用这个解决方案却得到了一个数组。 - Mujah Maskey
@PravatMaskey 不可能对对象的属性进行“排序”,但是可以对数组进行排序。 - Rich O'Kelly
@rich.okelly,是的,这就是我所说的。我忘记检查他的要求“sort”字典对象。所以我删除了我的答案:D。 - Mujah Maskey

2

编辑 - 我修改了脚本,如果属性类型相同,则合并属性:数字求和,字符串连接,对象递归合并。我没有包括排序,因为(引用此答案:Sorting JavaScript Object by property value

JavaScript对象的顺序是无序的(请参见ECMAScript语言规范第8.6节)。语言规范甚至不能保证,如果您连续两次迭代对象的属性,它们第二次出现的顺序与第一次相同。

如果您需要有序的内容,请使用数组和Array.prototype.sort方法。

function is_object(mixed_var) {
    if (Object.prototype.toString.call(mixed_var) === '[object Array]') {
        return false;
    }
    return mixed_var !== null && typeof mixed_var == 'object';
}

function merge(a, b) {
    var cache = {};
    cache = unpackObject(a, cache);
    cache = unpackObject(b, cache);
    return cache;


}

function unpackObject(a, cache) {
    for (prop in a) {
        if (a.hasOwnProperty(prop)) {
            if (cache[prop] === undefined) {
                cache[prop] = a[prop];
            } else {
                if (typeof cache[prop] === typeof a[prop]) {
                    if (is_object(a[prop])) {
                        cache[prop] = merge(cache[prop], a[prop]);
                    } else {
                        cache[prop] += a[prop];
                    }
                }
            }
        }
    }
    return cache;
}

var a = {
    en: 5,
    fr: 3,
    in : 9,
    lang: "js",
    object: {nestedProp: 6}

}
var b = {
    en: 8,
    fr: 21,
    br: 8,
    lang: "en",
    object: {nestedProp: 1, unique: "myne"}
}

var c = merge(a, b);

fiddle here http://jsfiddle.net/vyFN8/1/


@Jashwant 我更新了我的答案,你不能保证排序属性会按照那个顺序返回它们。 - Nicola Peluchetti
是的,我已经在stackoverflow的其他帖子上检查过,对象无法排序。但我们可以为它们创建对象数组。顺便说一句,你的解决方案很干净、强大。请添加排序代码,这样我就可以将其标记为答案。 - Jashwant
@Jashwant 只要你的属性是数字,你的排序算法就没问题。 - Nicola Peluchetti
你的回答太棒了。 - Albi

1
这是我的尝试,针对嵌套对象使用递归 - https://gist.github.com/greenafrican/19bbed3d8baceb0a15fd
// Requires jQuery
// Merge nested objects and if the properties are numbers then add them together, else
// fallback to jQuery.extend() result

function mergeObjectsAdd(firstObject, secondObject) {
    var result = $.extend(true, {}, firstObject, secondObject);
    for (var k in result) {
        if ("object" === typeof result[k]) {
            firstObject[k] = firstObject[k] || {};
            secondObject[k] = secondObject[k] || {};
            result[k] = mergeObjectsAdd(firstObject[k], secondObject[k]);
        } else {
            firstObject[k] = firstObject[k] || 0;
            secondObject[k] = secondObject[k] || 0;
            result[k] = ("number" === typeof firstObject[k] && "number" === typeof secondObject[k]) ? (firstObject[k] + secondObject[k]) : result[k];
        }
    }
    return result;
}

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