2012-02-10 41 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字節)轉換爲無符號長整型(8字節)。這將消除負數,因爲它們將從0開始。但是10之前的問題仍然存在。對於這一點,也許墊0或東西...

看起來這應該是可能的,但我今天的愚蠢和可以不聰明。

示例數據:
'貓'
'4'
'5.4'
'狗'
'-400'
'土豚'
'12 .23.34.54'
「我一句」
'0'

應該被選到:
'12 .23.34.54'
'土豚'
'貓'
'狗'
'我的一句話'
'-400'
'0'
'4'
'5.4'

+0

你能舉些例子值此列?它們只是附加了雙精度的文本嗎? – BACON 2012-02-10 16:57:46

+0

這裏可以發送比較器嗎? http://msdn.microsoft.com/en-us/library/cfttsh47.aspx – jrsconfitto 2012-02-10 17:07:14

+0

當您只有一個值時,比較函數不起作用。我可以從分類器當前正在評估的值中返回一個字符串。我會在我的描述中更加清楚 – MStodd 2012-02-10 17:31:31

回答

1

我懷疑你的東西叫做「自然排序順序」後是。 Attwood上有個帖子:http://www.codinghorror.com/blog/2007/12/sorting-for-humans-natural-sort-order.html

這篇文章中有幾個實現的例子。

+0

到目前爲止,您是最接近的。如果沒有人發佈算法,我會自己做更多的研究。 – MStodd 2012-02-10 17:45:30

+0

@MStodd:如果這是你想要的,你可以看看我對另一個問題的回答:http://stackoverflow.com/a/7205788/98607 – 2012-02-10 17:55:34

4

不是很有效的,但是一個簡單的比較算法,首先在數字和非數字之間進行分隔,然後在它們之間進行分類就可以工作 - 見下面的代碼。該inneficiency來自於事實,我們會做字符串雙變換了好幾次,所以你可以做的數字(即存放在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 2012-02-10 17:55:25

+0

不能使用比較。看到描述 – MStodd 2012-02-10 18:03:13

+1

看起來像每個人都downvoted。這真的很合適嗎?也許他們確實符合downvote箭頭上的「這個答案無用」標籤,但是直到最近的編輯,我們甚至不知道我們正在實現的函數的簽名。 – BACON 2012-02-10 18:09:55

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 2012-02-10 18:31:54

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); 
    } 
} 

通知的構造函數爲CustomString˚F 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(); 
    } 
}