HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpModerate

Merging two files containing sorted numbers into one

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
containingmergingintonumberstwofilesonesorted

Problem

The task is to merge 2 text files containing sorted integers into one file. Each number in both files is stored in new line(there are no empty lines,and only numbers are stored). Requirements are that if file1 has n numbers and file2 has m numbers, running time must be O(n+m) and memory consumption O(1).

My current solution is based on this algorithm:

while (not end of List A and not end of List B)
    if (List A current item <= List B current item)
        output List A current item
        advance List A index
    else
        output List B current item
        advance List B index

// At this point, one of the lists is empty.
// Output remaining items from the other
while (not end of List A)
    output List A current item
    advance List A index

while (not end of List B)
    output List B current item
    advance List B index


The solution is working fine, I've tested it for different cases.But, I'm looking for you opinion and suggestions, how can I improve the code, since it is quite huge.

Also, can I avoid assigning value to item 1 and item 2 variables at beginning? If I leave them unassigned, even if I pass them value visual studio is returning error saying that I'm using unassigned variables in while loop.

```
public static void Merge(string pathInputFile1, string pathInputFile2, string pathOutputFile)
{
int item1 = 0;
int item2 = 0;
bool endOfFile1 = false;
bool endOfFile2 = false;
string temp;

var inputFile1 = File.OpenText(pathInputFile1);
var inputFile2 = File.OpenText(pathInputFile2);
temp = inputFile1.ReadLine();
if (temp == null)
{
endOfFile1 = true;
}
else
{
item1 = Int32.Parse(temp);
}
temp = inputFile2.ReadLine();
if (temp == null)
{
endOfFile2 = true;
}
else
{
item2 = Int32.Parse(temp);
}

while (

Solution

As the others have pointed out, there is a lot of repetition in your code.
This is however a great opportunity to threat the files as simple iterators from which you can extract integers. This is done using a simple C# generator.

IEnumerator ToIterator(StreamReader reader)
{
   string line;
   while ((line = reader.ReadLine()) != null)
   {
      yield return Convert.ToInt32(line);
   }
}


Afterwards, simply follow the algorithm you initially described, but substitute List with an iterator (IEnumerator in .NET terms):

void MergeIntegersFiles(string source1, string source2, string destination)
{
   using (var reader1 = new StreamReader(source1))
   {
      using (var reader2 = new StreamReader(source2))
      {
         using (var writer = new StreamWriter(destination))
         {
            var iterator1 = ToIterator(reader1);
            var iterator2 = ToIterator(reader2);
            var iterator1StillAvailable = iterator1.MoveNext();
            var iterator2StillAvailable = iterator2.MoveNext();

            while (iterator1StillAvailable && iterator2StillAvailable)
            {
               if (iterator1.Current <= iterator2.Current)
               {
                  writer.WriteLine(iterator1.Current);
                  iterator1StillAvailable = iterator1.MoveNext();
               }
               else
               {
                  writer.WriteLine(iterator2.Current);
                  iterator2StillAvailable = iterator2.MoveNext();
               }
            }

            //check which iterator can still provide values
            var iteratorRemaining = iterator1StillAvailable
               ? iterator1
               : iterator2StillAvailable ? iterator2 : null;

            if (null != iteratorRemaining)
            {
               do
               {
                   writer.WriteLine(iteratorRemaining.Current);                      
               } while (iteratorRemaining.MoveNext());
            }
         }
      }
   }
}


Abstracting further, you create a method which combines arbitrary iterators of sorted values:

IEnumerable GetSortedValues(IEnumerator iterator1, IEnumerator iterator2) where T : IComparable
{
   var iterator1StillAvailable = iterator1.MoveNext();
   var iterator2StillAvailable = iterator2.MoveNext();

   while (iterator1StillAvailable && iterator2StillAvailable)
   {
      if (iterator1.Current.CompareTo(iterator2.Current) < 1)
      {
         yield return iterator1.Current;
         iterator1StillAvailable = iterator1.MoveNext();
      }
      else
      {
         yield return iterator2.Current;
         iterator2StillAvailable = iterator2.MoveNext();
      }
   }

   //check which iterator can still provide values
   var iteratorRemaining = iterator1StillAvailable
      ? iterator1
      : iterator2StillAvailable ? iterator2 : null;

   if (null != iteratorRemaining)
   {
      do
      {
         yield return iteratorRemaining.Current;
      } while (iteratorRemaining.MoveNext());
   }
}


Which leads you to a simple function that just iterates through something created from two stream readers and writer the result back:

void MergeIntegersFiles(string source1, string source2, string destination)
{
   using (var reader1 = new StreamReader(source1))
   {
      using (var reader2 = new StreamReader(source2))
      {
         using (var writer = new StreamWriter(destination))
         {
            foreach (var value in GetSortedValues(ToIterator(reader1), ToIterator(reader2)))
            {
               writer.WriteLine(value);
            }
         }
      }
   }
}

Code Snippets

IEnumerator<int> ToIterator(StreamReader reader)
{
   string line;
   while ((line = reader.ReadLine()) != null)
   {
      yield return Convert.ToInt32(line);
   }
}
void MergeIntegersFiles(string source1, string source2, string destination)
{
   using (var reader1 = new StreamReader(source1))
   {
      using (var reader2 = new StreamReader(source2))
      {
         using (var writer = new StreamWriter(destination))
         {
            var iterator1 = ToIterator(reader1);
            var iterator2 = ToIterator(reader2);
            var iterator1StillAvailable = iterator1.MoveNext();
            var iterator2StillAvailable = iterator2.MoveNext();

            while (iterator1StillAvailable && iterator2StillAvailable)
            {
               if (iterator1.Current <= iterator2.Current)
               {
                  writer.WriteLine(iterator1.Current);
                  iterator1StillAvailable = iterator1.MoveNext();
               }
               else
               {
                  writer.WriteLine(iterator2.Current);
                  iterator2StillAvailable = iterator2.MoveNext();
               }
            }

            //check which iterator can still provide values
            var iteratorRemaining = iterator1StillAvailable
               ? iterator1
               : iterator2StillAvailable ? iterator2 : null;

            if (null != iteratorRemaining)
            {
               do
               {
                   writer.WriteLine(iteratorRemaining.Current);                      
               } while (iteratorRemaining.MoveNext());
            }
         }
      }
   }
}
IEnumerable<T> GetSortedValues<T>(IEnumerator<T> iterator1, IEnumerator<T> iterator2) where T : IComparable<T>
{
   var iterator1StillAvailable = iterator1.MoveNext();
   var iterator2StillAvailable = iterator2.MoveNext();

   while (iterator1StillAvailable && iterator2StillAvailable)
   {
      if (iterator1.Current.CompareTo(iterator2.Current) < 1)
      {
         yield return iterator1.Current;
         iterator1StillAvailable = iterator1.MoveNext();
      }
      else
      {
         yield return iterator2.Current;
         iterator2StillAvailable = iterator2.MoveNext();
      }
   }

   //check which iterator can still provide values
   var iteratorRemaining = iterator1StillAvailable
      ? iterator1
      : iterator2StillAvailable ? iterator2 : null;

   if (null != iteratorRemaining)
   {
      do
      {
         yield return iteratorRemaining.Current;
      } while (iteratorRemaining.MoveNext());
   }
}
void MergeIntegersFiles(string source1, string source2, string destination)
{
   using (var reader1 = new StreamReader(source1))
   {
      using (var reader2 = new StreamReader(source2))
      {
         using (var writer = new StreamWriter(destination))
         {
            foreach (var value in GetSortedValues(ToIterator(reader1), ToIterator(reader2)))
            {
               writer.WriteLine(value);
            }
         }
      }
   }
}

Context

StackExchange Code Review Q#131627, answer score: 10

Revisions (0)

No revisions yet.