如何以不同方式合并JavaScript中的数组?

8
我希望以一种略有不同的方式合并数组。 我有2个或更多类似的数组:

var array1 = ["apple", "banana"];

var array2 = ["apple", "apple", "orange"];

我想要的输出是:

var array3 = ["apple", "apple", "banana", "orange"];

因此,如果任何一个给定的数组中有一个变量出现了多次,合并算法应该保留该数组中的所有变量。
我看到了一些可以防止重复的代码,但它们的输出结果如下:

var array3 = ["apple", "banana", "orange"];

更多的例子如下:

var arr1 = [1,2,3,4];

var arr2 = [1,1,2,4,5,5,5];

var arr3 = [1,3,3,5,5];

我想要的输出是:

var array4 = [1,1,2,3,3,4,5,5,5];

我该怎么做呢?

1
console.log(array1.concat(array2).sort()),这是您的意思吗? - Xotic750
2
@Xotic750:不行。这会保留每个元素实例,而不仅仅是数组中任意元素的最大数量。仔细看看他的期望输出,尤其是在最后一个示例中。他希望输出中有两个1,而不是四个。 - Robert Harvey
@RobertHarvey 哦,我明白了。(我想) - Xotic750
@u.zzz 你目前尝试了什么? - Xotic750
@Xotic750 我是一个初学者。我尝试了这个帖子中的算法,但它们并不像我想要的那样工作。 - u.zzz
即使作为一个初学者,你也应该尝试自己编写一些代码来完成任务,并在遇到困难时提出具体问题。 - Xotic750
5个回答

2
这是一种通过计算每个数组中每个项的出现次数来完成的方法:
var arr1 = [1,2,3,4];
var arr2 = [1,1,2,4,5,5,5];
var arr3 = [1,3,3,5,5];

function joinCommon(/* list of arrays */) {
    var arr, arrayCounts, masterList = {}, item, output;
    // for each array passed in
    for (var i = 0; i < arguments.length; i++) {
        arr = arguments[i];
        arrayCounts = {};
        // iterate each array
        for (var j = 0; j < arr.length; j++) {
            item = arr[j];
            if (!arrayCounts[item]) {
                arrayCounts[item] = 1;
            } else {
                ++arrayCounts[item];
            }
            // now keep master list and master counts
            if (!masterList[item]) {
                masterList[item] = {cnt: 1, val: item};
            } else {
                masterList[item].cnt = Math.max(masterList[item].cnt, arrayCounts[item]);
            }
        }
    }
    // now output result
    output = [];
    for (var i in masterList) {
        for (var j = 0; j < masterList[i].cnt; j++) {
            output.push(masterList[i].val);
        }
    }
    return output;    
}

var results = joinCommon(arr1, arr2, arr3);

工作演示:http://jsfiddle.net/jfriend00/dtn6zw4m/


["1","1","2","3","3","4","5","5","5"] !== [1,1,2,3,3,4,5,5,5] - Xotic750
1
@Xotic750 - 已经在修复这个问题了。对字符串值的处理很好,但数字被转换为字符串,因为所有属性名称都是字符串。现在通过将实际值与属性名称分开来跟踪修正了这个问题。 - jfriend00
我认为值得指出,并且我相信你会修复它。 ;) - Xotic750
仅供参考,这里有一个关于ECMA3和ECMA5解决方案的jsPerf - Xotic750

1

我喜欢在这方面使用ramda(http://ramdajs.com/docs/index.html)

var arr1 = [1,2,3,4];

var arr2 = [1,1,2,4,5,5,5];

var arr3 = [1,3,3,5,5];

var allArrays = [arr1, arr2, arr3];

var allValues = R.compose(R.uniq, R.flatten)(allArrays);

var getItemCounts = R.countBy(function(item) {
   return item;
});

var itemCounts = R.map(function(arr) {
   return getItemCounts(arr);
})(allArrays);

var combined = [];
R.forEach(function(item) {
   var countsForItem = R.pluck(item, itemCounts);
   var maxCount = R.max(countsForItem);
   combined.push.apply(combined, R.repeatN(item, maxCount));
})(allValues);

console.log(combined.sort());

JSFiddle: http://jsfiddle.net/pcr0q1xa/3/


JSFiddle:{{link1:http://jsfiddle.net/pcr0q1xa/3/}}。

[1,1,1,1,1,1,2,2,2,2,3,3,3,3,3] !== [1,1,2,3,3,4,5,5,5] - Xotic750
看起来好多了。 :) - Xotic750

1

Ramda 是你的好朋友。

function merge () {
  return R.chain(R.apply(R.repeat), R.toPairs(R.reduce(
    R.mergeWith(R.max),
    {},
    R.map(R.countBy(R.identity), arguments)
  )))
}

var array1 = ["apple", "banana"];

var array2 = ["apple", "apple", "orange"];

console.log(JSON.stringify(merge(array1, array2)))

var arr1 = [1,2,3,4];

var arr2 = [1,1,2,4,5,5,5];

var arr3 = [1,3,3,5,5];

console.log(JSON.stringify(merge(arr1, arr2, arr3)))
<script src="http://cdnjs.cloudflare.com/ajax/libs/ramda/0.22.1/ramda.min.js"></script>


1
这里是使用ECMA5的解决方案。
Javascript

function indexOf(items, value) {
    return items.map(function (subitem) {
        return subitem.value;
    }).indexOf(value);
}

function countItems(previous, item) {
    var atIndex = indexOf(previous, item);

    if (atIndex !== -1) {
        previous[atIndex].count += 1;
    } else {
        previous.push({
            value: item,
            count: 1
        });
    }

    return previous;
}

function mergeCounts(item) {
    var atIndex = indexOf(this, item.value);

    if (atIndex === -1) {
        this.push(item);
    } else if (this[atIndex].count < item.count) {
        this[atIndex] = item;
    }
}

function expandCounts(previous, item) {
    var iter;

    for (iter = 0; iter < item.count; iter += 1) {
        previous.push(item.value);
    }

    return previous;
}

function mergeArg(items, arg) {
    arg.reduce(countItems, []).forEach(mergeCounts, items);

    return items;
}

function mergeMaxItems() {
    return [].reduce.call(arguments, mergeArg, []).reduce(expandCounts, []);
}

var arr1 = [1, 2, 3, 4],
    arr2 = [1, 1, 2, 4, 5, 5, 5],
    arr3 = [1, 3, 3, 5, 5];

document.body.appendChild(document.createTextNode(mergeMaxItems(arr1, arr2, arr3)));


-1

未经测试且不是JS,但我认为这就是您要找的内容。 我刚刚手动测试过它,它可以通过您的测试用例。

while items in lista or items in listb
    compare a.head, b.head
    if a.head is smaller or b.is_empty then 
         append a.head to output
         a.drophead
    else if b.head is smaller or a.is_empty then
         append b.head to output
         b.drophead
    else
         append b.head to output
         b.drophead
         a.drophead

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