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?

6
  • 1
    console.log(array1.concat(array2).sort()), is that what you mean? Commented Jan 4, 2015 at 22:00
  • 2
    @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. Commented Jan 4, 2015 at 22:02
  • @RobertHarvey Aha, I see. (I think) Commented Jan 4, 2015 at 22:05
  • @u.zzz What have you tried so far? Commented Jan 4, 2015 at 22:13
  • @Xotic750 I am a beginner. I tried algorithms in this post but they are not working as I want. Commented Jan 4, 2015 at 22:17

5 Answers 5

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/

Sign up to request clarification or add additional context in comments.

4 Comments

["1","1","2","3","3","4","5","5","5"] !== [1,1,2,3,3,4,5,5,5]
@Xotic750 - 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.
Thought it was worth pointing out, and had every faith that you would fix it. ;)
And just for interest a jsPerf of ECMA3 vs ECMA5 solutions.
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)));

Comments

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/

2 Comments

[1,1,1,1,1,1,2,2,2,2,3,3,3,3,3] !== [1,1,2,3,3,4,5,5,5]
Seems much better. :)
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>

Comments

-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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.