Сортировка смешанных чисел и строк

для API> = 15 для API 23 другое решение не работает в моем случае. наконец я понял это.

 Intent nextScreen = new Intent(currentActivity.this, MainActivity.class);
 nextScreen.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | IntentCompat.FLAG_ACTIVITY_CLEAR_TASK);
 startActivity(nextScreen);
 ActivityCompat.finishAffinity(currentActivity.this);
15
задан Boris Callens 23 June 2009 в 15:20
поделиться

9 ответов

Возможно, вы могли бы пойти с более общим подходом и использовать алгоритм естественной сортировки , такой как реализация C # здесь .

12
ответ дан 1 December 2019 в 00:33
поделиться

На ум приходят два способа, но я не уверен, какой из них более эффективен. Внедрите собственный 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 ) );
21
ответ дан 1 December 2019 в 00:33
поделиться

Используйте другую перегрузку OrderBy , которая принимает параметр IComparer .

Затем вы можете реализовать свой собственный IComparer который использует int.TryParse , чтобы определить, число это или нет.

3
ответ дан 1 December 2019 в 00:33
поделиться

Я бы сказал, что вы можете разделить значения с помощью RegularExpression (предполагая, что все является int), а затем снова объединить их вместе.

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

Затем с помощью ваших двух списков вы можете отсортировать каждое из них а затем снова объедините их в любом формате.

2
ответ дан 1 December 2019 в 00:33
поделиться

Вы можете просто использовать функцию , предоставляемую Win32 API :

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

, и вызывать ее из IComparer , как показали другие.

2
ответ дан 1 December 2019 в 00:33
поделиться
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);
}

Он по-прежнему выполняет преобразование, но только один раз на элемент.

1
ответ дан 1 December 2019 в 00:33
поделиться

Вы можете использовать настраиваемый компаратор - тогда оператор упорядочения будет иметь следующий вид:

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

Хотя это может показаться немного многословным, оно инкапсулирует логику сортировки в надлежащий тип. Затем вы можете, если хотите, легко подвергнуть Comparer автоматическому тестированию (модульное тестирование). Его также можно использовать повторно.

(Возможно, удастся сделать алгоритм немного понятнее, но это было лучшее, что я мог быстро собрать.)

1
ответ дан 1 December 2019 в 00:33
поделиться

В некотором смысле вы также можете "обмануть". Основываясь на вашем описании проблемы, вы знаете, что любая строка длины 2 будет числом. Так что просто отсортируйте все строки длины 1. А затем отсортируйте все строки длины 2. А затем сделайте несколько перестановок, чтобы переупорядочить ваши строки в правильном порядке. По сути, процесс будет работать следующим образом: (при условии, что ваши данные находятся в массиве.)

Шаг 1: Протолкните все строки длиной 2 в конец массива. Отслеживайте, сколько у вас их есть.

Шаг 2: На месте отсортируйте строки длины 1 и строки длины 2.

Шаг 3: двоичный поиск «a», который будет на границе двух ваших половин .

Шаг 4: Замените ваши двузначные строки буквами по мере необходимости.

Тем не менее, хотя этот подход будет работать, он не включает регулярные выражения, и не пытается анализировать значения, отличные от int, как int - я не рекомендую это делать. Вы напишете значительно больше кода, чем уже предлагались другие подходы. Это запутывает суть того, что вы пытаетесь сделать. Это не сработает, если вы вдруг получите строки из двух букв или строк из трех цифр. И т.д. Я просто включаю это, чтобы показать, как можно по-другому взглянуть на проблемы и найти альтернативные решения.

1
ответ дан 1 December 2019 в 00:33
поделиться

Используйте преобразование Шварца для выполнения O (n) преобразований!

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));
}
1
ответ дан 1 December 2019 в 00:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: