Создание сортируемой строки из нескольких строк в .Net - PullRequest
0 голосов
/ 11 февраля 2011

Мне нужна функция, которая преобразует массив строк в строку, которая сортируется в том же порядке, как если бы вы сортировали входные данные (сортируйте первый входной аргумент, если равен второй, сортируйте и т. Д ...)

В нативном коде разделение строк с помощью \ 0 подойдет, но каким-то образом

("a" + char.MinValue + "2"). CompareTo ("a1") равно 1!

Что происходит и возможно ли создать такую ​​функцию?

public static string StringsToKey(params string[] values)

РЕДАКТИРОВАТЬ: Это тест, который я хочу пройти:

Assert.IsTrue(MiscUtils.StringsToKey("a", "2").CompareTo(MiscUtils.StringsToKey("a1")) < 0);

Я бы хотел избежать использования CompareOrdinal, потому что я не всегда контролирую, как будет сортироваться ключ. Кроме того, порядковый номер может привести к неправильному порядку сортировки на международных наборах ...

Ответы [ 5 ]

5 голосов
/ 11 февраля 2011
public static string StringsToKey(params string[] values)
{
    return string.Join(char.MinValue.ToString(), values);
}

использование должно работать:

var s1 = StringsToKey("abc", "def", "1234");
var s2 = StringsToKey("ab", "cde", "f1234");

var comparisonResult = string.Compare(s1, s2, StringComparison.Ordinal);

EDIT:

var s1 = StringsToKey("a", "2");
var s2 = StringsToKey("a1");

var r = string.Compare(s1, s2, StringComparison.Ordinal);

возвращает отрицательное значение (-49).

Редактировать II:

, как упоминал Джо в комментариях, это не будет работать, если строка содержит '\0' (или '\t', если табуляция используется как разделитель). Так что нет такого разделителя, который бы работал во всех случаях. Итак, я переписал функцию, но теперь она имеет 2 параметра, но все еще использует для сравнения CompareTo метод:

public static int CompareStringSequences(
    IEnumerable<string> first, 
    IEnumerable<string> second)
{
    var x = Enumerable.Zip(first, second, (s1, s2) => s1.CompareTo(s2))
        .FirstOrDefault(i => i != 0);

    return x == 0 ?
               x1.Length - x2.Length :
               x;
}
1 голос
/ 11 февраля 2011

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

Конечно, если вы уверены, что ваши строки не содержат нулей и порядковая сортировка в порядке, некоторые из уже опубликованных решений будут работать.

Но я бы сказал, даже не пытайтесь. Написание компаратора для ваших строковых массивов будет более эффективным (избегая создания временных строк) и лучше выражает ваши намерения. Есть много способов сделать это, в том числе следующие (сравнивает IList вместо string [] для большей гибкости и поддерживает сравнения, учитывающие культурные особенности):

public class StringListComparer : IComparer<IList<string>>
{
    private StringComparer comparer;

    public StringListComparer()
        : this(StringComparer.Ordinal)
    {
    }

    public StringListComparer(StringComparer comparer)
    {
        if (comparer == null) throw new ArgumentNullException("comparer");
        this.comparer = comparer;
    }

    public int Compare(IList<string> x, IList<string> y)
    {
        int result;
        for (int i = 0; i < Math.Min(x.Count, y.Count); i++)
        {
            result = comparer.Compare(x[i], y[i]);
            if (result != 0) return result;
        }
        return x.Count.CompareTo(y.Count);
    }
}

Приведенный выше код не проверен и, возможно, содержит ошибки, но я уверен, что вы поняли идею.

1 голос
/ 11 февраля 2011

По сути, если вы используете «порядковую сортировку», вы хотите просто объединить все строки. Для иллюстрации возьмем два массива строк:

a={"My", "dog", "is",     "black"}
b={"My", "cat", "sleeps", "often"}

Сравнение каждой строки в этих массивах, пока не найдете другую, отличающуюся:

a={"My", "dog", "is",     "black"}
b={"My", "cat", "sleeps", "often"}
          ^
          first difference: b is "less than" a

... в точности равно сравнению сцепленных строк:

a="Mydogisblack"
b="Mycatsleepsoften"
     ^
     First difference; again, b < a

Код вашего метода будет просто:

public static string StringsToKey(params string[] values)
{
    var builder = new StringBuilder();
    foreach(var s in values)
        builder.Append(s + "\0"); //the \0 is ASCII 0, used to delimit words
    return builder;
}
1 голос
/ 11 февраля 2011

Этот код работает для ситуаций, когда ввод не содержит вкладок:

    public static string StringsToKey(params string[] values)
    {
        return values.Aggregate("", (current, value) => current + value + '\t');
    }
1 голос
/ 11 февраля 2011

метод String.CompareTo () возвращает целое число, сравнивающее два значения с длиной самой короткой строки.Учитывая, что «\ 0» «меньше» символа «1», он следует за ним в обычном порядке строк.Вы можете использовать метод String.Split () , чтобы разделить любой символ.в том числе '\ 0'.Затем можно отсортировать строки с помощью метода Array.Sort () .

...