2012-02-10 3 views
4

У меня есть приложение, которое отображает набор данных, который позволяет мне вызывать пользовательский код .NET и зацикливаться на проблеме сортировки. Один столбец в моем наборе данных содержит как строки, так и числовые данные, и я хочу сортировать строки в алфавитном порядке и цифры численно. Все, что я могу сделать, это взять текущее значение, с которым работает сортировщик, и вернуть что-то..NET правильная сортировка удваивается как строки численно

Если мой список {"-6", "10", "5"}, я хочу создать строки из тех чисел, которые сортируют их по алфавиту. То, что я придумал это делает их все положительное, то заполнение нулями, например:

public object Evaluate(object currentValue) 
{ 
    //add 'a' to beginning of non-numbers, 'b' to beginning of numbers so that numbers come second 
    string sortOrder = ""; 
    if(!currentValue.IsNumber) 
     sortOrder = "a" + currentValue; 
    else 
    { 
     sortOrder = "b" 
     double number = Double.Parse(currentValue); 

     //add Double.MaxValue to our number so that we 'hopefully' get rid of negative numbers, but don't go past Double.MaxValue 
     number += (Double.MaxValue/2) 

     //pad with zeros so that 5 comes before 10 alphabetically: 
     //"0000000005" 
     //"0000000010" 
     string paddedNumberString = padWithZeros(number.ToString()) 


     //"b0000000005" 
     //"b0000000010" 
     sortOrder += paddedNumberString; 
    } 
} 

Проблема:
Если я просто вернуть номер, то они упорядочиваются в алфавитном порядке и 10 будет прийти до 5 , и я даже не знаю, что произойдет с отрицательными цифрами.

Решение:
Один взломанный, который я думал, пытался преобразовать из удвоений (8 байтов) в unsigned longs (8 bytes). Это избавит нас от отрицательных чисел, так как они начнутся с нуля. Проблема 10 до 5 лет все еще остается. Для этого, возможно, пэд с 0s или что-то еще ...

Кажется, что это должно быть возможно, но сегодня у меня есть немой и не может быть умным.

пример данных:
'кошка'
'4'
'5.4'
'собака'
'-400'
'муравьед'
'12 .23.34.54'
«Я предложение»
'0'

, которые должны быть отсортированы по:
'12 .23.34.54'
'муравьед'
'кошка'
'собака'
'я предложение'
'-400'
'0'
'4'
'5.4'

+0

Можете ли вы дать несколько примеров значения для этого столбца? Это просто текст с двойным добавлением? – BACON

+0

Можно ли отправить здесь компас? http://msdn.microsoft.com/en-us/library/cfttsh47.aspx – jrsconfitto

+0

Функции сравнения не работают, когда у вас есть только одно значение. Я могу вернуть строку из значения, которое оценивает сортировщик. Я сделаю это более ясным в своем описании – MStodd

ответ

1

Я подозреваю, что вы после этого что-то под названием 'Natural Сортировка'. У Attwood есть на нем сообщение: http://www.codinghorror.com/blog/2007/12/sorting-for-humans-natural-sort-order.html

В сообщении есть несколько примеров реализаций.

+0

До сих пор вы ближе всего. Я сделаю больше исследований самостоятельно, если никто не опубликует алгоритм. – MStodd

+0

@MStodd: Если это то, что вы хотите, вы можете взглянуть на мой ответ на другой вопрос: http://stackoverflow.com/a/7205788/98607 –

4

Не очень эффективный, но простой алгоритм сравнения, который сначала разделяет числа и не номера, тогда сортировки между ними будут работать - см. код ниже. Недостаток исходит из того, что мы сделаем строку для двойного преобразования довольно много раз, поэтому вы можете сделать предварительную обработку чисел (т. Е. Сохранить их двойные значения в List<double?>), а затем использовать их, а не всегда делать синтаксический анализ.

public class StackOverflow_9231493 
{ 
    public static void Test() 
    { 
     List<string> list = new List<string> 
     { 
      "cat", 
      "4", 
      "5.4", 
      "dog", 
      "-400", 
      "aardvark", 
      "12.23.34.54", 
      "i am a sentence", 
      "0" , 
     }; 

     list.Sort(new Comparison<string>(delegate(string s1, string s2) 
     { 
      double d1, d2; 
      bool isNumber1, isNumber2; 
      isNumber1 = double.TryParse(s1, out d1); 
      isNumber2 = double.TryParse(s2, out d2); 
      if (isNumber1 != isNumber2) 
      { 
       return isNumber2 ? -1 : 1; 
      } 
      else if (!isNumber1) 
      { 
       return s1.CompareTo(s2); 
      } 
      else 
      { 
       return Math.Sign(d1 - d2); 
      } 
     })); 

     Console.WriteLine(string.Join("\n", list)); 
    } 
} 

Обновления на основе комментариев:

Если вы хотите, чтобы только вернуть то, что без использования компаратора напрямую, вы можете использовать ту же логику, но обернуть значение типа, который знает, как это сделать сравнение, как вы хотите, как показано ниже.

public class StackOverflow_9231493 
{ 
    public class Wrapper : IComparable<Wrapper> 
    { 
     internal string value; 
     private double? dbl; 

     public Wrapper(string value) 
     { 
      if (value == null) throw new ArgumentNullException("value"); 
      this.value = value; 
      double temp; 
      if (double.TryParse(value, out temp)) 
      { 
       dbl = temp; 
      } 
     } 

     public int CompareTo(Wrapper other) 
     { 
      if (other == null) return -1; 
      if (this.dbl.HasValue != other.dbl.HasValue) 
      { 
       return other.dbl.HasValue ? -1 : 1; 
      } 
      else if (!this.dbl.HasValue) 
      { 
       return this.value.CompareTo(other.value); 
      } 
      else 
      { 
       return Math.Sign(this.dbl.Value - other.dbl.Value); 
      } 
     } 
    } 
    public static void Test() 
    { 
     List<string> list = new List<string> 
     { 
      "cat", 
      "4", 
      "5.4", 
      "dog", 
      "-400", 
      "aardvark", 
      "12.23.34.54", 
      "i am a sentence", 
      "0" , 
     }; 

     List<Wrapper> list2 = list.Select(x => new Wrapper(x)).ToList(); 
     list2.Sort(); 
     Console.WriteLine(string.Join("\n", list2.Select(w => w.value))); 
    } 
} 
+0

Downvoter, не забывайте про комментировать почему? – carlosfigueira

+0

Нельзя использовать сравнение. См. Описание – MStodd

+1

Похоже, что все получили вниз. Это действительно уместно? Возможно, они имеют право на ярлык «Этот ответ не полезен» на стрелке вниз, но до самого последнего редактирования мы даже не знали подписи функции, которую мы выполняли. – BACON

0

Я предполагаю, что ваши данные типа string и не object.Следующая функция может быть вызвана с помощью Comparison<string> delegate.

static int CompareTo(string string1, string string2) 
{ 
    double double1, double2; 

    // Add null checks here if necessary... 

    if (double.TryParse(string1, out double1)) 
    { 
     if (double.TryParse(string2, out double2)) 
     { 
      // string1 and string2 are both doubles 

      return double1.CompareTo(double2); 
     } 
     else 
     { 
      // string1 is a double and string2 is text; string2 sorts first 

      return 1; 
     } 
    } 
    else if (double.TryParse(string2, out double2)) 
    { 
     // string1 is text and string2 is a double; string1 sorts first 

     return -1; 
    } 
    else 
    { 
     // string1 and string2 are both text 

     return string1.CompareTo(string2); 
    } 
} 

Вы можете проверить это следующим образом:

static void Main(string[] args) 
{ 
    var list = new List<string>() { 
     "cat", 
     "4", 
     "5.4", 
     "dog", 
     "-400", 
     "aardvark", 
     "12.23.34.54", 
     "i am a sentence", 
     "0" 
    }; 

    list.Sort(CompareTo); 
    foreach (var item in list) 
     Console.WriteLine(item); 
} 
+0

Невозможно использовать сравнение. Надеюсь, мои правки сделают цель более ясной. – MStodd

2

У меня есть для вас решение, но оно требует произвольный, фиксированный максимальный размер строки, но не требует никакой другой информации о наборе

Во-первых, определить пользовательский набор символов следующим образом:

public class CustomChar 
{ 
    public static readonly int Base; 
    public static readonly int BitsPerChar; 

    public char Original { get; private set; } 
    public int Target { get; private set; } 

    private static readonly Dictionary<char, CustomChar> Translation; 

    private static void DefineOrderedCharSet(string charset) 
    { 
     foreach (var t in charset) 
     { 
      new CustomChar(t); 
     } 
    } 

    static CustomChar() 
    { 
     Translation = new Dictionary<char, CustomChar>(); 
     DefineOrderedCharSet(",-.aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ"); 
     BitsPerChar = (int)Math.Ceiling(Math.Log(Translation.Count, 2)); 
     Base = (int) Math.Pow(2, BitsPerChar); 
    } 

    private CustomChar(char original) 
    { 
     Original = original; 

     if(Translation.Count > 0) 
     { 
      Target = Translation.Max(x => x.Value.Target) + 1; 
     } 
     else 
     { 
      Target = 0; 
     } 

     Translation[original] = this; 
    } 

    public static CustomChar Parse(char original) 
    { 
     return Translation[original]; 
    } 
} 

Тогда определяют конструкцию для обработки преобразования из строки в System.Numeric.BigInteger следующим

public class CustomString 
{ 
    public string String { get; private set; } 
    public BigInteger Result { get; private set; } 
    public const int MaxChars = 600000; 

    public CustomString(string source) 
    { 
     String = source; 
     Result = 0; 

     for (var i = 0; i < String.Length; i++) 
     { 
      var character = CustomChar.Parse(String[i]); 
      Result |= (BigInteger)character.Target << (CustomChar.BitsPerChar * (MaxChars - i - 1)); 
     } 

     double doubleValue; 

     if (!double.TryParse(source, out doubleValue)) 
     { 
      return; 
     } 

     Result = new BigInteger(0x7F) << (MaxChars * CustomChar.BitsPerChar); 
     var shifted = (BigInteger)(doubleValue * Math.Pow(2, 32)); 
     Result += shifted; 
    } 

    public static implicit operator CustomString(string source) 
    { 
     return new CustomString(source); 
    } 
} 

Обратите внимание на CTOR для CustomString е inds удваивает и дополняет их представления BigInteger, чтобы упорядочить объекты для сортировки числовых значений.

Это довольно быстрый бросок вместе, но получает ваш описанный выход из теста:

class Program 
{ 
    public static string[] Sort(params CustomString[] strings) 
    { 
     return strings.OrderBy(x => x.Result).Select(x => x.String).ToArray(); 
    } 

    static void Main() 
    { 
     var result = Sort(
      "cat", 
      "4", 
      "5.4", 
      "dog", 
      "-400", 
      "aardvark", 
      "12.23.34.54", 
      "i am a sentence", 
      "0"); 

     foreach (var str in result) 
     { 
      Console.WriteLine(str); 
     } 

     Console.ReadLine(); 
    } 
} 
Смежные вопросы