4

I write this function for merging two arrays.

private static int[] Merge(int[] array1, int[] array2)
{
    var mergedArray = new int[array1.Length + array2.Length];
    int i = 0, j = 0, k = 0;
    while(k < mergedArray.Length)
    {
        if(i == array1.Length || j == array2.Length)
        {
             if (i <= j)
                {
                    mergedArray[k] = array1[i];
                    i++;
                }
                else
                {
                    mergedArray[k] = array2[j];
                    j++;
                }
        }
        else
        {
            if(array1[i] < array2[j])
            {
                mergedArray[k] = array1[i];
                i++;
            }
            else
            {
                mergedArray[k] = array2[j];
                j++;
            }
        }
        k++;
    }
    return mergedArray;
}

How to reduce if statements in this code?

9
  • This is not the same question. The merging here takes into account the values of the arrays, while the possible duplicate only concatenates two arrays. Commented Jul 11, 2012 at 20:33
  • I think this does more than just merge. Maybe there some sorting going on (if(array1[i]<array2[j]).. statement). Can you please explain more what are you trying to do. Commented Jul 11, 2012 at 20:33
  • 7
    "Is there a way to simplify this code?" --> codereview.stackexchange.com Commented Jul 11, 2012 at 20:36
  • 1
    Related stackoverflow.com/questions/9807701/… Commented Jul 11, 2012 at 20:40
  • 1
    @Victor: Please provide an example set of arrays, and what the expected results are. The intent is not clear from the source what the Merge() function actually does. Commented Jul 11, 2012 at 20:46

6 Answers 6

12

You can also make a Linq friendly version. This one is fast and will work on IEnumerable. You could easily translate this to any type T where T is IComparable.

    private static IEnumerable<int> Merge(IEnumerable<int> enum1, IEnumerable<int> enum2)
    {
        IEnumerator<int> e1 = enum1.GetEnumerator();
        IEnumerator<int> e2 = enum2.GetEnumerator();

        bool remaining1 = e1.MoveNext();
        bool remaining2 = e2.MoveNext();

        while (remaining1 || remaining2)
        {
            if (remaining1 && remaining2)
            {
                if (e1.Current > e2.Current)
                {
                    yield return e2.Current;
                    remaining2 = e2.MoveNext();
                }
                else
                {
                    yield return e1.Current;
                    remaining1 = e1.MoveNext();
                }
            }
            else if (remaining2)
            {
                yield return e2.Current;
                remaining2 = e2.MoveNext();
            }
            else
            {
                yield return e1.Current;
                remaining1 = e1.MoveNext();
            }
        }
    }
Sign up to request clarification or add additional context in comments.

Comments

10

Not as good as the Linq solution, but if you want the traditional if-then style function you could write:

private static int[] Merge(int[] array1, int[] array2)
{
    var mergedArray = new int[array1.Length + array2.Length];
    int i = 0, j = 0, k = 0;
    while(k < mergedArray.Length)
    {
        if (j == array2.Length || ((i < array1.Length) && (array[i] < array2[j])))
        {
            mergedArray[k] = array1[i];
            i++;
        }
        else
        {
            mergedArray[k] = array2[j];
            j++;
        }
        k++;
    }
    return mergedArray;
}

(edit: missing brace added)

Or in English:

If array2 is empty or if there are still values in array 1 and array1[i] is less than array2[j], then take value from array1, otherwise take from array 2

Or very concise (just for fun):

private static int[] Merge(int[] array1, int[] array2)
{
    var mergedArray = new int[array1.Length + array2.Length];
    int i = 0, j = 0;
    while(i+j < mergedArray.Length)
        if (j == array2.Length || ((i < array1.Length) && (array1[i] < array2[j])))
            mergedArray[i+j] = array1[i++];
        else
            mergedArray[i+j] = array2[j++];
    return mergedArray;
}

5 Comments

This is much better than the Linq solution (you can measure the speed if you want). That said you must put the j == array2.Length test at the beginning of your test. ie if (j == array2.Length || ((i < array1.Length) && (array[i] < array2[j]))) otherwise, array2[j] could throw an exception
Would this also assume that the two arrays passed in as parameters are already ordered?
@ErikPhilips the question asker assumes so, yes
@jcolebrand I feel like i'm blind, where did he mention the arrays are pre-sorted?
the title How to optimize function for merging sorted arrays in C#
4

Linq is your friend, here is one way:

private static int[] Merge(int[] array1, int[] array2)
{
  List<int> merged = new List<int>(array1.Length + array2.Length);
  merged.AddRange(array1);
  merged.AddRange(array2);
  return merged.GroupBy(x => x)
               .Select(x => x.Key)
               .OrderBy(x => x)
               .ToArray();
}

6 Comments

Array.Copy(array1, newArray, array1.Length);
I think that arrays more faster than Lists
@Victor You can entirely avoid copy if you yous array1.Concat(array2) instead of merge. Linq consumes anyway 'IEnumerable' so the Concat will first enumerate through array1 and then through array2.
@Victor if the two arrays can contain the same values, this solution removes the duplicates. So while your Merge(new [] {2,3,4}, new [] {4,5,6}) will results in 2,3,4,4,5,6 until Erik's solution results in 2,3,4,5,6
@Victor By the way is avoiding conditionals really your ultimative goal? You solution has O(n) time - with proposed Linq you will get O(n log(n)) because it involves OrderBy. Are you really happy with the trade-off?
|
2

Rather than being specific to arrays, I'd suggest looking at the following question for a number of different answers for IEnumerable<T>. Most efficient algorithm for merging sorted IEnumerable<T>

The answer I provided at https://stackoverflow.com/a/14444706/184528 has only one if statement and merge multiple enumerables.

For example to use it to merge three different arrays:

    public static void Main(string[] args)
    {
        var xs = new[] { 1, 5, 9 };
        var ys = new[] { 2, 7, 8 };
        var zs = new[] { 0, 3, 4, 6 };

        foreach (var a in new [] { xs, ys, zs }.Merge())
            Console.WriteLine(a);
    }

Comments

0

It seems to me that this is the most "reduced" version of the function:

private static int[] Merge(int[] array1, int[] array2)
{
    return array1.Concat(array2).OrderBy(x => x).ToArray();
}

It can't get much simpler than this. Not one if remaining.

I ran this Merge(new [] { 1, 2, 2, 3 }, new [] { 1, 1, 2, 4, }) on both the original code and my solution and got the same answer.

Comments

-1

How about using Queue<int> to drain the arrays:

    private static int[] Merge(int[] array1, int[] array2)
    {
        int N=array1.Length+array2.Length;
        Queue<int> Q1=new Queue<int>(array1);
        Queue<int> Q2=new Queue<int>(array2);
        Queue<int> result=new Queue<int>(N);

        for(int k=0; k<N; k++)
        {
            if(Q1.Count==0)
            {
                result.Enqueue(Q2.Dequeue());
            }
            else if(Q2.Count==0)
            {
                result.Enqueue(Q1.Dequeue());
            }
            else
            {
                result.Enqueue(
                    Q1.Peek()<Q2.Peek()?
                    Q1.Dequeue():
                    Q2.Dequeue());
            }
        }
        return result.ToArray();
    }

1 Comment

You are copying arrays at least 3 times.

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.