【问题标题】:Get Max() of alphanumeric value获取字母数字值的 Max()
【发布时间】:2011-09-01 09:44:00
【问题描述】:

我有一个包含字母数字 ID(例如 a10a10 和 d10a9)的字典,我想要最大的 ID,即 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 的最大值?

【问题讨论】:

  • 您的问题是,如果按字母数字排序,则 d10a10 排在第一位,因为 1 排在 9 之前。根据您的逻辑,d4a13 是否应该排在这两个之前?
  • ID 的第一部分总是“d10a”吗?
  • 首先,长度相关 x1 在 a10 之前,其次任何组合都是可能的,没有像 d10a 这样的前缀

标签: c# .net linq max


【解决方案1】:

我认为你可以尝试推出自己的IComparer&lt;string&gt;

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;

【讨论】:

【解决方案2】:

假设 ID 总是以“d10a”开头,这就像一个魅力:

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

【讨论】:

  • lsd.Select(kvp =&gt; kvp.Value.Substring(4)).Select(i =&gt; Int32.Parse(i)).Max() 使其更具可读性
【解决方案3】:

一种方法是这样做

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

但是我认为这种方法会评估每个项目的最大长度,因此您最好先将其提取到变量中

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 数字并转换为 max 函数的 long,然后再次转换回来以获取最大字符串。

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 扩展方法而不是 Max,因为这可以让您执行所有比较逻辑....

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

这可能没有空检查,但您可以轻松添加它们。

【讨论】:

    【解决方案4】:

    我想如果你这样做:

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

    它可能会给你你想要的。

    【讨论】:

      【解决方案5】:

      你需要 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;
          }
      

      【讨论】:

        【解决方案6】:

        好的 - 我认为您需要首先将每个键变成一系列字符串和数字 - 因为您需要整数才能确定比较。然后你实现了一个 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
        }
        

        现在您可以在IEnumerable&lt;ValueChain&gt;FirstOrDefault 上使用OrderByDescending 获得最大值:

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

        因此您可以使用它对字典中的字符串值进行排序:

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

        【讨论】:

          猜你喜欢
          • 2016-11-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-09-18
          • 2016-07-02
          • 2022-08-18
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多