2011-09-01 66 views
3

的MAX()我有,我想最大的ID的字典方含ID这是字母数字(例如a10a10 & d10a9),这意味着9 一...获取字符值

当我使用以下代码,d10a9是MAX,因为9已排序10之前

var lsd = new Dictionary<string, string>(); 
lsd.Add("a", "d10a10"); 
lsd.Add("b", "d10a9"); 
string max = lsd.Max(kvp => kvp.Value); 

如何获得最长字符串组合的ID的最大值?

+0

你的问题是,如果排序的字母数字,d10a10至上,因为1 9排序前应d4a13这两个根据您的逻辑之前进行排序? –

+2

是ID总是“d10a”的第一部分? – Tsar

+0

首先,长度是相关的x1在a10之前,第二任何组合都是可能的,没有像d10a这样的前缀 –

回答

1

一种方法是做到这一点

string max =lsd.Where(kvp=>kvp.Value.Length==lsd.Max(k=>k.Value.Length)).Max(kvp => kvp.Value); 

但是我认为这个方法将evalute最大长度为每个项目,所以你可能会更好地将其解压缩到一个变量第一

int maxLength=lsd.Max(kvp=>kvp.Value.Length); 
string max = lsd.Where(kvp=>kvp.Value.Length == maxLength).Max(kvp => kvp.Value); 

如果您打算在那里有空字符串,您可能还需要执行空检查

int maxLength=lsd.Max(kvp=>(kvp.Value??String.Empty).Length); 
string max = lsd.Where(kvp=>(kvp.Value??String.Empty).Length == maxLength).Max(kvp => kvp.Value); 

或者将您的字符串作为Base36数字,并将其转换为long来表示max函数,然后再转换回来以获取最大字符串。

string max =lsd.Max(tvp=>tvp.Value.FromBase36()).ToBase36(); 

public static class Base36 { 

    public static long FromBase36(this string src) { 
    return src.ToLower().Select(x=>(int)x<58 ? x-48 : x-87).Aggregate(0L,(s,x)=>s*36+x); 
    } 

    public static string ToBase36(this long src) { 
    StringBuilder result=new StringBuilder(); 
    while(src>0) { 
     var digit=(int)(src % 36); 
     digit=(digit<10) ? digit+48 :digit+87; 
     result.Insert(0,(char)digit); 
     src=src/36; 
     } 
    return result.ToString(); 
    } 
} 

最后仅仅只是Agregate扩展方法,而不是最大,因为这可以让你做所有的比较逻辑....

lsd.Agregate(string.Empty,(a,b)=> a.Length == b.Length ? (a>b ? a:b) : (a.Length>b.Length ? a:b)); 

这可能没有null检查,但是您可以轻松地将它们添加英寸

+0

就像一个魅力:-) –

3

我想你可能会尝试推出自己的IComparer<string>

class HumanSortComparer : IComparer<string> 
{ 
    public int Compare(string x, string y) 
    { 
     // your human sorting logic here 
    } 
} 

用法:

var last = collection.OrderBy(x => x.Value, new HumanSortComparer()).LastOrDefault(); 
if (last != null) 
    string max = last.Value; 
+1

这是正确的,但仅仅为了澄清OrderByDescending和FirstOrDefault也会起作用。 –

+0

请参阅(在LINQ效率中的简短案例研究)[http://msmvps.com/blogs/jon_skeet/archive/2005/10/02/a-short-case-study-in-linq-efficiency.aspx] orderby/firstorDefault组合效率不高。 –

0

我认为,如果你这样做:

var max = lsd.OrderByDescending(x => x.Value) 
    .GroupBy(x => x.Value.Length) 
    .OrderByDescending(x => x.Key) 
    .SelectMany(x => x) 
    .FirstOrDefault(); 

它可能给你什么你想。

2

这就像一个魅力假设的ID始终以 “D10A” 开始:

int max = lsd.Max(kvp => Convert.ToInt32(kvp.Value.Substring(4))); 
Console.Write(string.Format("d10a{0}", max)); 
+0

或'lsd.Select(kvp => kvp.Value.Substring(4))。Select(i => Int32.Parse(i))。Max()'使其更具可读性 – abatishchev

0

你需要StringComparer.OrdinalIgnoreCase。

不需要使用linq,这样做的功能非常简单。 复杂性当然是O(n)。

public static KeyValuePair<string, string> FindMax(IEnumerable<KeyValuePair<string, string>> lsd) 
    { 
     var comparer = StringComparer.OrdinalIgnoreCase; 
     var best = default(KeyValuePair<string, string>); 
     bool isFirst = true; 
     foreach (KeyValuePair<string, string> kvp in lsd) 
     { 
      if (isFirst || comparer.Compare(kvp.Value, best.Value) > 0) 
      { 
       isFirst = false; 
       best = kvp; 
      } 
     } 
     return best; 
    } 
0

好吧 - 我认为您需要先将每个键转换为一系列字符串和数字 - 因为您需要整个数字才能确定比较结果。然后你实现一个IComparer - 我已经用两个输入字符串以及其他几个字符串测试了它,并且它看起来像你想要的那样。表现可能会有所改善 - 但我正在集思广益!

创建这个类:

public class ValueChain 
{ 
    public readonly IEnumerable<object> Values; 
    public int ValueCount = 0; 

    private static readonly Regex _rx = 
    new Regex("((?<alpha>[a-z]+)|(?<numeric>([0-9]+)))", 
     RegexOptions.Compiled | RegexOptions.IgnoreCase); 

    public ValueChain(string valueString) 
    { 
    Values = Parse(valueString); 
    } 

    private IEnumerable<object> Parse(string valueString) 
    { 
    var matches = _rx.Matches(valueString); 
    ValueCount = matches.Count; 

    foreach (var match in matches.Cast<Match>()) 
    { 
     if (match.Groups["alpha"].Success) 
     yield return match.Groups["alpha"].Value; 
     else if (match.Groups["numeric"].Success) 
     yield return int.Parse(match.Groups["numeric"].Value); 
    } 
    } 
} 

现在这个比较器:

public class ValueChainComparer : IComparer<ValueChain> 
{ 
    private IComparer<string> StringComparer; 
    public ValueChainComparer() 
    : this(global::System.StringComparer.OrdinalIgnoreCase) 
    { 

    } 

    public ValueChainComparer(IComparer<string> stringComparer) 
    { 
    StringComparer = stringComparer; 
    } 

    #region IComparer<ValueChain> Members 

    public int Compare(ValueChain x, ValueChain y) 
    { 
    //todo: null checks 
    int comparison = 0; 
    foreach (var pair in x.Values.Zip 
     (y.Values, (xVal, yVal) => new { XVal = xVal, YVal = yVal })) 
    { 
     //types match? 
     if (pair.XVal.GetType().Equals(pair.YVal.GetType())) 
     { 
     if (pair.XVal is string) 
      comparison = StringComparer.Compare(
      (string)pair.XVal, (string)pair.YVal); 
     else if (pair.XVal is int) //unboxing here - could be changed 
      comparison = Comparer<int>.Default.Compare(
      (int)pair.XVal, (int)pair.YVal); 
     if (comparison != 0) 
      return comparison; 
     } 
     else //according to your rules strings are always greater than numbers. 
     { 
     if (pair.XVal is string) 
      return 1; 
     else 
      return -1; 
     } 
    } 

    if (comparison == 0) //ah yes, but were they the same length? 
    { 
     //whichever one has the most values is greater 
     return x.ValueCount == y.ValueCount ? 
     0 : x.ValueCount < y.ValueCount ? -1 : 1; 
    } 

    return comparison; 
    } 

    #endregion 
} 

现在你可以使用OrderByDescending最高上IEnumerable<ValueChain>FirstOrDefault

[TestMethod] 
public void TestMethod1() 
{ 
    List<ValueChain> values = new List<ValueChain>(new [] 
          { 
           new ValueChain("d10a9"), 
           new ValueChain("d10a10") 
          }); 

    ValueChain max = 
    values.OrderByDescending(v => v, new ValueChainComparer()).FirstOrDefault(); 
} 

这样你就可以使用它来排序你的di中的字符串值ctionary:

var maxKvp = lsd.OrderByDescending(kvp => new ValueChain(kvp.Value), 
         new ValueChainComparer()).FirstOrDefault();