2009-06-23 15 views
11

मेरे पास स्ट्रिंग्स की एक सूची है जिसमें एक int (अधिकतम 2 अंक) का अक्षर या स्ट्रिंग प्रस्तुति हो सकती है। उन्हें संख्यात्मक मूल्य पर क्रमबद्ध रूप से क्रमबद्ध करने की आवश्यकता है या (जब यह वास्तव में एक int है) यह प्रतिनिधित्व करता है।मिश्रित संख्याओं और तारों को छंटनी

उदाहरण:

IList<string> input = new List<string>() 
    {"a", 1.ToString(), 2.ToString(), "b", 10.ToString()}; 

input.OrderBy(s=>s) 
    // 1 
    // 10 
    // 2 
    // a 
    // b 

मैं क्या चाहते हो जाएगा

// 1 
    // 2 
    // 10 
    // a 
    // b 

मैं अगर यह इसके साथ स्वरूपित करने के लिए एक सफल TryParse है, यह पार्स करने का प्रयास तो साथ प्रारूपित से जुड़े कुछ पता नहीं है है मेरी अपने कस्टम स्ट्रिंगफॉर्मर को इसके पहले शून्य बनाने के लिए है। मैं कुछ और सरल और प्रदर्शन करने की उम्मीद कर रहा हूं।

संपादित
मैं एक IComparer मैं बाद में उपयोग के लिए मेरे Utils पुस्तकालय में फेंक दिया बनाने समाप्त हो गया।
जबकि मैं उस पर था, मैंने भी मिश्रण में युगल फेंक दिया।

public class MixedNumbersAndStringsComparer : IComparer<string> { 
    public int Compare(string x, string y) { 
     double xVal, yVal; 

     if(double.TryParse(x, out xVal) && double.TryParse(y, out yVal)) 
      return xVal.CompareTo(yVal); 
     else 
      return string.Compare(x, y); 
    } 
} 

//Tested on int vs int, double vs double, int vs double, string vs int, string vs doubl, string vs string. 
//Not gonna put those here 
[TestMethod] 
public void RealWorldTest() 
{ 
    List<string> input = new List<string>() { "a", "1", "2,0", "b", "10" }; 
    List<string> expected = new List<string>() { "1", "2,0", "10", "a", "b" }; 
    input.Sort(new MixedNumbersAndStringsComparer()); 
    CollectionAssert.AreEquivalent(expected, input); 
} 

उत्तर

12

शायद आप अधिक सामान्य दृष्टिकोण के साथ जा सकते हैं और natural sorting एल्गोरिदम का उपयोग कर सकते हैं जैसे सी # कार्यान्वयन here

+0

ठंडा।यदि पहले पता था तो इसका इस्तेमाल होता: पी –

+1

वास्तव में बहुत अच्छा है, मुझे इसके लिए एक डेल्फी रैपर भी मिला है http://irsoft.de/web/strnatcmp-and-natsort-for-delphi –

+0

यह सभी मामलों में काम नहीं करेगा । मान लें कि ypu में आइटमों की निम्न सूची है: "0/30" "0/248" "0/496" "0/357.6"। सॉर्टिंग के बाद यह ऑर्डर रखेगा, जो आप उम्मीद नहीं कर सकते हैं। –

2

मैं कहना चाहता हूँ कि तुम एक रेग्युलर ऍक्सप्रैशन का उपयोग कर मूल्यों को विभाजित कर सकता है (यह मानते हुए सब कुछ एक पूर्णांक है) और फिर उन्हें एक साथ पुन: शामिल।

//create two lists to start 
string[] data = //whatever... 
List<int> numbers = new List<int>(); 
List<string> words = new List<string>(); 

//check each value 
foreach (string item in data) { 
    if (Regex.IsMatch("^\d+$", item)) { 
     numbers.Add(int.Parse(item)); 
    } 
    else { 
     words.Add(item); 
    } 
} 

तब आपके दो सूचियों के साथ आप उनमें से प्रत्येक सॉर्ट कर सकते हैं और फिर उन्हें वापस में एक साथ आप चाहते हैं जो कुछ भी प्रारूप मर्ज करें।

+0

हाँ, यह मेरे दृष्टिकोण से सरल है। +1 –

3

OrderBy के अन्य अधिभार का उपयोग करें जो IComparer पैरामीटर लेता है।

फिर आप अपने IComparer को लागू कर सकते हैं जो int.TryParse का उपयोग यह बताता है कि यह संख्या है या नहीं।

0
public static int? TryParse(string s) 
{ 
    int i; 
    return int.TryParse(s, out i) ? (int?)i : null; 
} 

// in your method 
IEnumerable<string> input = new string[] {"a", "1","2", "b", "10"}; 
var list = input.Select(s => new { IntVal = TryParse(s), String =s}).ToList(); 
list.Sort((s1, s2) => { 
    if(s1.IntVal == null && s2.IntVal == null) 
    { 
     return s1.String.CompareTo(s2.String); 
    } 
    if(s1.IntVal == null) 
    { 
     return 1; 
    } 
    if(s2.IntVal == null) 
    { 
     return -1; 
    } 
    return s1.IntVal.Value.CompareTo(s2.IntVal.Value); 
}); 
input = list.Select(s => s.String); 

foreach(var x in input) 
{ 
    Console.WriteLine(x); 
} 

यह अभी भी रूपांतरण करता है, लेकिन केवल एक बार/आइटम।

17

दो तरीके मन के लिए सुनिश्चित जो अधिक performant है आते हैं, नहीं। एक कस्टम IComparer लागू:

class MyComparer : IComparer<string> 
{ 
    public int Compare(string x, string y) 
    { 
     int xVal, yVal; 
     var xIsVal = int.TryParse(x, out xVal); 
     var yIsVal = int.TryParse(y, out yVal); 

     if (xIsVal && yIsVal) // both are numbers... 
      return xVal.CompareTo(yVal); 
     if (!xIsVal && !yIsVal) // both are strings... 
      return x.CompareTo(y); 
     if (xIsVal)    // x is a number, sort first 
      return -1; 
     return 1;    // x is a string, sort last 
    } 
} 

var input = new[] {"a", "1", "10", "b", "2", "c"}; 
var e = input.OrderBy(s => s, new MyComparer()); 

या, संख्याओं और गैर संख्या में अनुक्रम को विभाजित, तो प्रत्येक उपसमूह को सॉर्ट, अंत में क्रमबद्ध परिणामों में शामिल होने के; कुछ की तरह:

var input = new[] {"a", "1", "10", "b", "2", "c"}; 

var result = input.Where(s => s.All(x => char.IsDigit(x))) 
        .OrderBy(r => { int z; int.TryParse(r, out z); return z; }) 
        .Union(input.Where(m => m.Any(x => !char.IsDigit(x))) 
           .OrderBy(q => q)); 
+0

आपका आईसीओएमपेयर सही (वर्णमाला) क्रम में गैर-संख्यात्मक तारों को वापस नहीं करता है। आपकी LINQ क्वेरी करता है। – LukeH

+0

हाँ, धन्यवाद, मैं इसे ठीक कर दूंगा। – LBushkin

+0

मैंने ओपी में अपना अंतिम कोड जोड़ा। स्ट्रिंग चीज भी देखी। इसके अलावा मैंने हर पार्स से पहले शॉर्टकटिंग की कोशिश की। यह नहीं पता कि यह बहुत अधिक प्रदर्शन करता है, लेकिन मुझे इसे पुन: व्यवस्थित करने के लिए उतना ही प्रयास किया गया क्योंकि यह मुझे परीक्षण करने के लिए ले जाएगा;) –

1

आप एक कस्टम comparer इस्तेमाल कर सकते हैं - आदेश बयान तो होगा:

var result = input.OrderBy(s => s, new MyComparer()); 

जहां MyComparer इस तरह परिभाषित किया गया है:

public class MyComparer : Comparer<string> 
{ 
    public override int Compare(string x, string y) 
    { 

     int xNumber; 
     int yNumber; 
     var xIsNumber = int.TryParse(x, out xNumber); 
     var yIsNumber = int.TryParse(y, out yNumber); 

     if (xIsNumber && yIsNumber) 
     { 
      return xNumber.CompareTo(yNumber); 
     } 
     if (xIsNumber) 
     { 
      return -1; 
     } 
     if (yIsNumber) 
     { 
      return 1; 
     } 
     return x.CompareTo(y); 
    } 
} 

यद्यपि यह एक लग सकता है बिट वर्बोज़, यह सॉर्टिंग तर्क को एक उचित प्रकार में encapsulates। यदि आप चाहें तो आप आसानी से तुलनात्मक को स्वचालित परीक्षण (इकाई परीक्षण) के अधीन कर सकते हैं। यह भी पुन: प्रयोज्य है।

(यह एल्गोरिथ्म थोड़ा स्पष्ट करने के लिए संभव हो सकता है, लेकिन यह सबसे अच्छा मैं जल्दी से एक साथ फेंक सकता था।)

0

तुम भी "धोखा" कर सकता है कुछ अर्थों में।समस्या के आपके विवरण के आधार पर, आप जानते हैं कि लंबाई 2 की कोई स्ट्रिंग एक संख्या होगी। तो बस लंबाई की सभी स्ट्रिंग्स को क्रमबद्ध करें 1. और उसके बाद लंबाई 2 के सभी स्ट्रिंग्स को सॉर्ट करें। और फिर सही क्रम में अपने स्ट्रिंग को फिर से ऑर्डर करने के लिए स्वैपिंग का एक गुच्छा करें। अनिवार्य रूप से प्रक्रिया निम्नानुसार काम करेगी: (मान लें कि आपका डेटा सरणी में है।)

चरण 1: सरणी के अंत तक लंबाई 2 की सभी स्ट्रिंग पुश करें। आपके पास कितने हैं इसका ट्रैक रखना।

चरण 2: जगह प्रकार लंबाई 1 के तार और लंबाई 2.

चरण 3 की स्ट्रिंग्स में: जो अपने दो हिस्सों की सीमा पर होगा 'एक' के लिए बाइनरी खोज।

चरण 4: आवश्यकतानुसार अक्षरों के साथ अपने दो अंकों स्ट्रिंग को स्वैप करें।

यह कहा गया है कि, यह दृष्टिकोण काम करेगा, नियमित अभिव्यक्तियों को शामिल नहीं करता है, और गैर-int मानों को एक int के रूप में पार्स करने का प्रयास नहीं करता - मैं इसकी अनुशंसा नहीं करता हूं। आप पहले से सुझाए गए अन्य दृष्टिकोणों की तुलना में काफी अधिक कोड लिखेंगे। यह उस बिंदु के बारे में बताता है जो आप करने की कोशिश कर रहे हैं। यदि आप अचानक दो अक्षर स्ट्रिंग्स या तीन अंकों स्ट्रिंग्स प्राप्त करते हैं तो यह काम नहीं करता है। आदि। मैं यह दिखाने के लिए बस यह भी शामिल कर रहा हूं कि आप अलग-अलग समस्याओं को कैसे देख सकते हैं, और वैकल्पिक समाधानों के साथ आ सकते हैं।

2

तुम बस समारोह provided by the Win32 API इस्तेमाल कर सकते हैं:

[DllImport ("shlwapi.dll", CharSet=CharSet.Unicode, ExactSpelling=true)] 
static extern int StrCmpLogicalW (String x, String y); 

और यह एक IComparer से फोन के रूप में दूसरों से पता चला है।

1

ओ (एन) रूपांतरण करने के लिए Schwartzian Transform का उपयोग करें!

private class Normalized : IComparable<Normalized> { 
    private readonly string str; 
    private readonly int val; 

    public Normalized(string s) { 
    str = s; 

    val = 0; 
    foreach (char c in s) { 
     val *= 10; 

     if (c >= '0' && c <= '9') 
     val += c - '0'; 
     else 
     val += 100 + c; 
    } 
    } 

    public String Value { get { return str; } } 

    public int CompareTo(Normalized n) { return val.CompareTo(n.val); } 
}; 

private static Normalized In(string s) { return new Normalized(s); } 
private static String Out(Normalized n) { return n.Value; } 

public static IList<String> MixedSort(List<String> l) { 
    var tmp = l.ConvertAll(new Converter<String,Normalized>(In)); 
    tmp.Sort(); 
    return tmp.ConvertAll(new Converter<Normalized,String>(Out)); 
} 
+0

वास्तव में इतना आसान नहीं है कि मैंने जो कुछ भी मुझे पोस्ट किया है, उसके लिए मैंने पोस्ट किया है। अधिक प्रदर्शनकारी हो सकता है, लेकिन सादगी पर perf डालने के लिए पर्याप्त महत्वपूर्ण नहीं है –

0

मुझे एक ही समस्या थी और यहां उतरा: तारों को सॉर्ट करना जिनमें निम्नलिखित उदाहरण के रूप में एक संख्यात्मक प्रत्यय है।

मूल:

"Test2", "Test1", "Test10", "Test3", "Test20" 

डिफ़ॉल्ट तरह परिणाम:

"Test1", "Test10", "Test2", "Test20", "Test3" 

वांछित तरह परिणाम:

public class NaturalComparer : IComparer 
{ 

    public NaturalComparer() 
    { 
     _regex = new Regex("\\d+$", RegexOptions.IgnoreCase); 
    } 

    private Regex _regex; 

    private string matchEvaluator(System.Text.RegularExpressions.Match m) 
    { 
     return Convert.ToInt32(m.Value).ToString("D10"); 
    } 

    public int Compare(object x, object y) 
    { 
     x = _regex.Replace(x.ToString, matchEvaluator); 
     y = _regex.Replace(y.ToString, matchEvaluator); 

     return x.CompareTo(y); 
    } 
} 
:

"Test1", "Test2", "Test3, "Test10", "Test20" 

मैं एक कस्टम comparer का उपयोग कर समाप्त हो गया

एचटीएच; ओ)

संबंधित मुद्दे