views:

340

answers:

7

I have a list of names and phone numbers like so:

var phonelist = List<string[]> 
{
     new string[] {"Bill", "1234", "12345", "12314" },
     new string[] {"Bob", "", "12345", "12314" },
     new string[] {"Chris", "", "", "12314" },
     new string[] {"Dave", "1234", "", "12314" },
     new string[] {"Andy", "1234", "12345", "" },
}

What is the most efficient/elegant way to process this list so that the 'empty' numbers get filled from the right ?

Note, the arrays should stay the same length, like so:

var phonelist = List<string[]> 
{
     new string[] {"Bill", "1234", "12345", "12314" },
     new string[] {"Bob", "12345", "12314", "" },
     new string[] {"Chris", "12314", "", "" },
     new string[] {"Dave", "1234", "12314", "" },
     new string[] {"Andy", "1234", "12345", "" },
}
+1  A: 

You could do something like that :

Comparison<string> comparison = (x,y) =>
{
    if (String.IsNullOrEmpty(x))
        return 1;
    if (String.IsNullOrEmpty(y))
        return -1;
    return String.Compare(x,y);
}

foreach (string[] array in phoneList)
{
    Array.Sort(array, comparison);
}

However you will need to tune the comparison logic a bit to keep the names before the numbers


EDIT : since the name seems to always be the first element, another option is to exclude it from the sort. No Array.Sort overload takes a Comparison<T> and a range, so you have to use a IComparer<T> instead :

class MyComparer : IComparer<string>
{
    public int Compare(string x, string y)
    {
     if (String.IsNullOrEmpty(x))
      return 1;
     if (String.IsNullOrEmpty(y))
      return -1;
     return String.Compare(x,y);
    }
}


foreach (string[] array in phonelist)
{
    Array.Sort(array, 1, array.Length - 1, new MyComparer());
}
Thomas Levesque
This might work better if the last line of your comparison is return 0 - any two items that are not empty are considered equal and don't need sorting. (Not tested this approach!)
Richard
This won't give the desired result.
0xA3
@Richard : no, it doesn't work (I just tried)
Thomas Levesque
Why the down-vote ? please explain what's wrong...
Thomas Levesque
Nice, but doesn't pass the test...
Benjol
Your code compiles but it *sorts* the arrays instead of shifting blank entries.
0xA3
+1  A: 

This should work. I'm not that knownledged at C#, but the idea is there

foreach(string[] person in phonelist)
{
 string[] newPerson = {"","","",""};
 int index = 0;
 for(int i=0; i<4; i++)
 {
  if(!String.IsNullOrEmpty(person[i])) newPerson[index++] = person[i];
 }
 person = newPerson;
}
Eric
@Eric - I'd use !String.IsNullOrEmpty(person[i]) over person[i]!="" if i were you. Also you're making a second array, is this really necesary?
Stevo3000
you don't need a pointer here, `newPerson` should be declared as `string[]`
Thomas Levesque
Thanks. Changes made.
Eric
+1  A: 

simple and ugly :(

    for(var x=0;x<phonelist.Count;x++)
    {
        var strings = phonelist[x];

        var l = strings.Length;
        var newAr=new string[l];
        var k = 0;

        for (var i = 0; i < l; )
        {
            if(strings[i]!="")
            {
                newAr[k++] = strings[i];
            }
            i++;
        }

        for (; k < l; k++)
            newAr[k] = "";

        phonelist[x] = newAr;
    }
TheVillageIdiot
Works, but slower than @renatm and @najmeddine
Benjol
+2  A: 

for each array cell, check if its empty and swap it with cell+1, if it's still empty swap it with cell+2.. when cell becomes not empty do the same thing with cell+2...

    int j;

    foreach (string[] strs in phoneList)
    {
        for (int i = 0; i < strs.Length; i++)
        {
            j = 1;
            while (string.IsNullOrEmpty(strs[i]) && j < strs.Length - i)
            {
                if (!string.IsNullOrEmpty(strs[i + j])) // to not swap 2 empty strings
                {
                    strs[i] = strs[i + j];
                    strs[i + j] = "";
                }
                j++;
            }
        }
    }
najmeddine
+1 for correct answer. Tho I'd use String.IsNullOrEmpty over == "".
Stevo3000
I fixed that, thanks.
najmeddine
There are plenty of sorting facilities in the framework... why not use them instead of a code which is error prone and hardly understandable ?
Thomas Levesque
Marginally faster than @renatm (with my test data)
Benjol
A: 
List<string[]> sorted = new List<string[]>();

foreach (string[] entry in phoneList)
{
    List<string> nonEmpty = (from s in entry
          where String.IsNullOrEmpty(s) == false
          select s).ToList();

    int pad = entry.Length - nonEmpty.Count;

    List<string> pads = new List<string>();

    while (pad > 0)
    {
     pads.Add(String.Empty);
        --pad;
    }

    List<string> sortedEntry = new List<string>();
    sortedEntry.AddRange(nonEmpty);
    sortedEntry.AddRange(pads);

    sorted.Add(sortedEntry.ToArray());
}
TJB
System.OutOfMemoryException in your while :)
Benjol
LOL! Fixed! Thanx!
TJB
+2  A: 

public static void PutEmptyStringsToTheEnd(string[] array) {
    int j = 0;
    for (int i = 0; i < array.Length; ++i)
        if (array[i].Length > 0)
            array[j++] = array[i];
    while (j < array.Length)
        array[j++] = "";
}

Call this function for each List element.

I voted this up because it works, and it's quite fast.
Benjol
Good thing I don't work with you: what is i and j? Why are you inlining a self-incrementing operator within an value declaration? It doesn't make things faster, if that's what you thought.
orlandu63
@orlandu63i and j are indexes in the array. I'm not sure what did you mean in the second sentence, but I didn't intend to optimize anything. I just wrote code in a way that IMO is the simplest to read and understand. And I'm also happy that I don't work with you :)
A: 

This is cooler

List<string[]> result = phonelist.Select(per => per.OrderBy(txt=>txt.Length==0).ToArray()).ToList();
Jon Spokes