У меня есть List<string>
, где записи представляют собой местоположения, такие как «010101», «010102», «010201», ..., со следующим значением:
Обычная сортировка показывает список, подобный следующему:
010101
010102
010103
010201
010202
020101
020102
020201
020202
020203
020204
...
Я хотел бы пользовательскую сортировку, где:
на этаже "01" сортировка производится обычным образом:
010101
010102
010103
010201
010202
на этаже "02" сортировка выполняется в обратном порядке для полосы, но не для входа в полосу, поэтому что-то вроде:
...
020201
020202
020203
020204
020101
020102
Здесь на сайте я нашел IEnumerable
в качестве решения, но это работает только для List<T>
, где T
— новый класс. Здесь это не имеет значения, так как я имею дело с «простыми» строками.
Я также нашел Enumerable
в качестве решения, но это тоже не работает, потому что список возможных строк слишком велик (не поддается перечислению), и я бы предпочел гибкое решение.
У кого-нибудь есть идея?
заранее спасибо
Я имею в виду, что я искал на сайте решение. Решения, которые я нашел, были неприменимы либо потому, что я не могу создать новый класс, либо потому, что у меня слишком много строк, и я не могу использовать подход Enumerable
.
Ваше описание решений настолько расплывчато, что мы действительно не можем сказать, являются ли они разумной отправной точкой или нет. Мы не знаем, что вы подразумеваете под «перечислимым подходом» или каковы ваши требования. (Сколько строк и что пойдет не так, когда вы попробуете то, что вы называете «перечислимым подходом»?)
Я нашел сообщение человека, пытающегося отсортировать список "C", "D", "G", "T" как "D", "G", "T", "C". Для этого был создан Enumerable
на основе этих четырех символов. Этот подход (который я называю подходом Enumerable
) здесь невозможен, так как количество возможных строк слишком велико.
Нет, экземпляр Enumerable
точно не создавался, потому что Enumerable — статический класс. Знаете ли вы, что List<T>
реализует IEnumerable<string>
? До сих пор неясно, пытались ли вы предоставить собственное сравнение с List<T>.Sort
, если честно...
Там больше двух этажей?
@Enigmativity: Нет, их всего два, поэтому достаточно ответа Стефана Бауэра.
Есть несколько способов добиться желаемого поведения, например. используя Сравнение<string>:
var input = new List<string>() { "010101", "010102", "010103", "010201", "010202", "020101", "020102", "020201", "020202", "020203", "020204" };
Comparison<string> comparison = (s1, s2) =>
{
if (s1 == null) throw new ArgumentNullException(nameof(s1));
if (s2 == null) throw new ArgumentNullException(nameof(s2));
if (s1.Length != 6 || s2.Length != 6) throw new ArgumentOutOfRangeException();
// Compare floor (first 2 digits)
var floor1 = s1.Substring(0, 2);
var floor2 = s2.Substring(0, 2);
var result = floor1.CompareTo(floor2);
if (result != 0) return result;
// Compare lane (middle 2 digits)
var lane1 = s1.Substring(2, 2);
var lane2 = s2.Substring(2, 2);
// Special case: in floor 02, reverse order of lanes
result = floor1 == "02"
? lane2.CompareTo(lane1)
: lane1.CompareTo(lane2);
if (result != 0) return result;
// Compare entry (last 2 digits)
return s1.Substring(4, 2).CompareTo(s2.Substring(4, 2));
};
// Sort the list using the comparison defined before
input.Sort(comparison);
Этот подход отлично подходит для сортировки строк, но представленная проблема кажется лучше решенной с помощью объекта для отслеживания пола, полосы и входа в полосу. Также было бы проще манипулировать этими полями, и результат переопределял бы .ToString для представления того, что они хотят.
Не рекомендуется возвращать -result
, чтобы инвертировать результат — если result
равно int.MinValue
, то -result
также будет int.MinValue
. Вероятно, самое простое надежное изменение — это return -Math.Sign(result)
. Альтернативой может быть использование result = floor1 == "02"? lane2.CompareTo(lane1) : lane1.CompareTo(lane2);
Если ваш ввод хорошо подобран (т.е. все цифры и все 6 символов), то это простой способ сделать это:
var input = new List<string>()
{
"010101", "010102", "010103", "010201", "010202",
"020101", "020102", "020201", "020202", "020203", "020204"
};
var output =
input
.Select(x => new
{
location = x,
floor = int.Parse(x.Substring(0, 2)),
lane = int.Parse(x.Substring(2, 2)),
entry = int.Parse(x.Substring(4, 2))
})
.OrderBy(x =>
x.floor * 10000
+ (x.floor % 2 == 0 ? 99 - x.lane : x.lane) * 100
+ x.entry)
.Select(x => x.location)
.ToList();
Это производит:
010101
010102
010103
010201
010202
020201
020202
020203
020204
020101
020102
Ваш ответ - отличное расширение, основанное на нечетных/четных номерах этажей. Спасибо.
Похоже, вам нужно реализовать
IComparer<string>
(или делегатComparison<string>
) и передать это вызовуList<T>.Sort
. Трудно понять, что вы подразумеваете под «IEnumerable
как решение» —IEnumerable
— это просто интерфейс. (Точно так же «Я также нашелEnumerable
в качестве решения» — это просто класс, содержащий методы расширения.) По сути, я подозреваю, что вам лучше всего определить класс для представления информации в строках и анализа перед сортировкой, но это немного другое дело - более насущная проблема в том, что ваш вопрос очень неясен.