Получить Max () буквенно-цифрового значения - PullRequest
3 голосов
/ 01 сентября 2011

У меня есть идентификатор контактного словаря, который является буквенно-цифровым (например, a10a10 и d10a9), из которого мне нужен самый большой идентификатор, то есть 9 <10 <a ... </p>

Когда я использую следующий код, 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);

Как получить максимальное значение идентификаторов с самой длинной строкой?

Ответы [ 6 ]

3 голосов
/ 01 сентября 2011

Я думаю, что вы можете попробовать бросить свой собственный 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;
2 голосов
/ 01 сентября 2011

это работает как брелок, предполагая, что идентификаторы всегда начинаются с "d10a":

int max = lsd.Max(kvp => Convert.ToInt32(kvp.Value.Substring(4)));
Console.Write(string.Format("d10a{0}", max));
1 голос
/ 01 сентября 2011

Одним из способов было бы сделать это

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 и преобразуйте в long для функции max, а затем выполните обратное преобразование.снова, чтобы получить строку 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 вместо Max, поскольку это позволяет вам выполнять всю логику сравнения ....

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

Это можетне имеет нулевых проверок, но вы легко можете их добавить.

0 голосов
/ 01 сентября 2011

Хорошо - я думаю, вам нужно сначала превратить каждую клавишу в последовательность строк и чисел - поскольку вам нужно целое число, чтобы можно было определить сравнение.Затем вы реализуете 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();
}

Так что вы можете использовать это для сортировки строковых значений в вашем словаре:

var maxKvp = lsd.OrderByDescending(kvp => new ValueChain(kvp.Value), 
                      new ValueChainComparer()).FirstOrDefault();
0 голосов
/ 01 сентября 2011

Вам нужен 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 голосов
/ 01 сентября 2011

Я думаю, что если вы сделали это:

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

Это может дать вам то, что вы хотите.

...