How to merge arrays in javascript in a different way?
Asked Answered
H

5

8

I want to merge arrays a little bit different way. I have 2 or more arrays like:

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

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

I want the output:

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

So if any given array has a variable in it more than once, merge algorithm should keep all of them from that array.

I saw some code that prevents duplication but it gives outputs like this:

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

for more example:

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

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

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

I want the output:

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

How can I do this?

Horace answered 4/1, 2015 at 21:50 Comment(6)
console.log(array1.concat(array2).sort()), is that what you mean?Engelbert
@Xotic750: No. That would preserve every instance of an element, not just the greatest number of an element in any array. Look at his desired outputs carefully, especially in the last example. He wants two 1's, in the output, not four.Downcome
@RobertHarvey Aha, I see. (I think)Engelbert
@Horace What have you tried so far?Engelbert
@Engelbert I am a beginner. I tried algorithms in this post but they are not working as I want.Horace
Even as a beginner, you should try some code of your own to accomplish the task and ask specific question when you become stuck.Engelbert
M
2

Here's one way to do it by counting the occurrences of each item in each array:

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);

Working demo: http://jsfiddle.net/jfriend00/dtn6zw4m/

Monazite answered 4/1, 2015 at 22:15 Comment(4)
["1","1","2","3","3","4","5","5","5"] !== [1,1,2,3,3,4,5,5,5]Engelbert
@Engelbert - was already working on fixing that. Worked fine for string values, but numbers were converted to strings because all property names are strings. Fixed now by keeping track of the actual value separately from the property name.Monazite
Thought it was worth pointing out, and had every faith that you would fix it. ;)Engelbert
And just for interest a jsPerf of ECMA3 vs ECMA5 solutions.Engelbert
C
1

I like to use ramda (http://ramdajs.com/docs/index.html) for this stuff

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/

Coordination answered 4/1, 2015 at 22:29 Comment(2)
[1,1,1,1,1,1,2,2,2,2,3,3,3,3,3] !== [1,1,2,3,3,4,5,5,5]Engelbert
Seems much better. :)Engelbert
E
1

Here is a solution using 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)));
Engelbert answered 5/1, 2015 at 2:18 Comment(0)
T
1

Ramda is your friend.

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>
Tabling answered 14/8, 2016 at 6:49 Comment(0)
S
-1

Untested and not JS, but I think this is what you are looking for. I have just tested it manually, it worked for your test cases.

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
Salvo answered 4/1, 2015 at 22:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.