Sorted (список, ключ = itemgetter) неправильно сортирует список элементов в Python

#python #list #sorting

#python #Список #сортировка

Вопрос:

 list1 = ["1.11", "2.0.0", "1.2", "2", "0.1", "1.2.1", "1.1.1", "2.0"]

from operator import itemgetter

def solution(data):

    parse_list = []
    parse_list2 = []
    final_solution = []
    for x in data:
        y = x.split('.')
        if len(y) == 1:
            x = "{}.{}.{}".format(y[0], '-1', '-1')
            parse_list.append(x)
        if len(y) == 2:
            x = "{}.{}.{}".format(y[0], y[1], '-1')
            parse_list.append(x)
        if len(y) == 3:
            x = "{}.{}.{}".format(y[0], y[1], y[2])
            parse_list.append(x)
    print(parse_list)

    parse_list2 = sorted(parse_list, key=itemgetter(0, 1, 2))

    # print(parse_list)
    print(parse_list2)

    def unparse(x):

        y = x.split('.')
        if int(y[1]) < 0:
            x = '{}'.format(y[0])
            final_solution.append(x)
            return
        if int(y[2]) == -1:
            x = '{}.{}'.format(y[0], y[1],)
            final_solution.append(x)
            return
        if int(y[2]) >= 0:
            x = '{}.{}.{}'.format(y[0], y[1], y[2])
            final_solution.append(x)
            return
    for x in parse_list2:
        x = unparse(x)
    print(final_solution)
  

вывод:

 ['1.11.-1', '2.0.0', '1.2.-1', '2.-1.-1', '0.1.-1', '1.2.1', '1.1.1', '2.0.-1']
['0.1.-1', '1.11.-1', '1.1.1', '1.2.-1', '1.2.1', '2.-1.-1', '2.0.0', '2.0.-1']
['0.1', '1.11', '1.1.1', '1.2', '1.2.1', '2', '2.0.0', '2.0']

  

Я не понимаю, почему

 parse_list2 = sorted(parse_list, key=itemgetter(0, 1, 2))
  

не сортирует вывод в

 ['1.11.-1', '2.0.0', '1.2.-1', '2.-1.-1', '0.1.-1', '1.2.1', '1.1.1', '2.0.-1']
['0.1.-1', '1.1.1', '1.2.-1', '1.2.1', '1.11.-1', '2.-1.-1', '2.0.0', '2.0.-1']
['0.1', '1.1.1', '1.2', '1.2.1', '1.11', '2', '2.0.0', '2.0']
  

потому что 2 меньше 11

Пожалуйста, кто-нибудь может помочь мне настроить мой код для работы (в отличие от предоставления мне другого, но функционального кода) или объяснить мне, почему моя логика не будет работать.

Комментарии:

1. "11" < "2" это True потому "1" < "2" , что. Если вы хотите сравнить цифры, вам нужно либо преобразовать их в целые числа, либо в числа с плавающей точкой, чтобы они правильно сортировались.

2. Добро пожаловать в SO! Строки упорядочены лексикографически. Разберите их на целые числа. Цель здесь просто отсортировать эти строки версии, которые имеют отрицательные числа? В чем описание проблемы? Конечно, есть лучший способ решить эту проблему, что бы это ни было…

3. Все, что вы делаете, это сортировка по первым 3 символам каждой строки. Он не пытается интерпретировать многозначные поля. Итак, для '1.11.-1' он использует ('1', '.', '1') в качестве ключа сортировки.

4. Может кто-нибудь дать мне указатель на превращение их в целые числа? Я пытался, но столкнулся с проблемой, что мне не нравится, когда я пытаюсь изменить список на тип integer.

5. itemgetter(0, 1, 2) получает первый, второй и третий символ в строке, а не числа. Почему бы вам не сохранить их как кортежи целых (int(y[0]), int(y[1]), int(y[2])) и просто отсортировать их (не требуется специального ключевого аргумента)?

Ответ №1:

Ваши значения не отсортированы, потому что они сравнивают строки, а не целые числа.

Вот как я бы отсортировал их вместо этого:

 def toIntList(x):
    intList = []
    for value in x.split("."):
        intList.append(int(value))
    return intList

list1 = ["1.11", "2.0.0", "1.2", "2", "0.1", "1.2.1", "1.1.1", "2.0"]

# Using a function
sort1 = sorted(list1, key=toIntList)

# Using a lambda-function:
sort2 = sorted(list1, key=lambda x: [int(i) for i in x.split(".")])

print(list1) # Output: ['1.11', '2.0.0', '1.2', '2', '0.1', '1.2.1', '1.1.1', '2.0']
print(sort1) # Output: ['0.1', '1.1.1', '1.2', '1.2.1', '1.11', '2', '2.0', '2.0.0']
print(sort2) # Output: ['0.1', '1.1.1', '1.2', '1.2.1', '1.11', '2', '2.0', '2.0.0']
  

Комментарии:

1. Хороший подход! Автор попросил не предоставлять другое решение.

Ответ №2:

Несколько советов:

  • import всегда в начале (я удалил его, потому что я его больше не использую, но всегда сохраняю их в верхней части файла, обычно три блока импорта: сначала стандартные библиотеки, затем сторонние библиотеки и третий импорт в другие файлы вашей программы)
  • 2 пустые строки между объявлениями функции первого уровня и класса
  • elif не будет пытаться выполнить условие, если предыдущее было истинным, поэтому мы избегаем ненужной проверки условий.
  • parse_list собирается хранить кортежи из трех целых чисел, такие как (1, 11, -1) .
  • parse_list = sorted(parse_list) эквивалентно parse_list.sort() который делает это на месте.
  • unparse Функция будет возвращать каждое значение вместо добавления в final_solution список, который принадлежит solution функции, а не unparse функции.
 def solution(data):
    parse_list = []
    for x in data:
        y = x.split('.')
        if len(y) == 1:
            parse_list.append((int(y[0]), -1, -1))
        elif len(y) == 2:
            parse_list.append((int(y[0]), int(y[1]), -1))
        elif len(y) == 3:
            parse_list.append((int(y[0]), int(y[1]), int(y[2])))
    print(parse_list)

    parse_list.sort()
    print(parse_list)

    def unparse(x):
        if x[1] < 0:
            return '{}'.format(x[0])
        if x[2] < 0:
            return '{}.{}'.format(x[0], x[1])
        return '{}.{}.{}'.format(x[0], x[1], x[2])

    final_solution = []
    for x in parse_list:
        final_solution.append(unparse(x))
    print(final_solution)


if __name__ == '__main__':
    list1 = ["1.11", "2.0.0", "1.2", "2", "0.1", "1.2.1", "1.1.1", "2.0"]
    solution(list1)
  

Это также может быть оптимизировано:

 def solution(data):
    parse_list = sorted([tuple(map(int, item.split('.'))) for item in data])
    final_solution = ['.'.join(map(str, item)) for item in parse_list]
    print(final_solution)


if __name__ == '__main__':
    list1 = ["1.11", "2.0.0", "1.2", "2", "0.1", "1.2.1", "1.1.1", "2.0"]
    solution(list1)
  

Давайте разберем это решение:

  1. [doSomethingWith(item) for item in data] создает список, в котором каждый элемент является тем, что doSomethingWith(item) есть, используя каждый раз один из элементов data .
  2. doSomethingWith(item) в нашем случае tuple(map(int, item.split('.'))) :
    1. item например "1.11"
    2. item.split('.') выдает нам список строк: ["1", "11"]
    3. map(f, iterable) применяет функцию f к каждому элементу iterable и возвращает объект, подобный списку (это не совсем список, но похожий). В нашем случае f это int так, что мы преобразуем подстроки в целые числа: [1, 11] <- На самом деле не список, но похожий
    4. Мы преобразуем этот объект, подобный списку, в кортеж с внешним tuple() : (1, 11)
  3. Внешний sorted() будет сортировать этот список, не нужно беспокоиться о недостающих элементах, так как (1, 0) < (1, 0, 0) они будут отсортированы должным образом.
  4. Итак, parse_list это уже отсортированный список кортежей целых чисел: [(0, 1), (1, 1, 1), ...]
  5. Снова [doSomethingWith(item) for item in parse_list] оператор, поэтому мы создаем новый список, что-то делая с каждым элементом в parse_list .
  6. doSomethingWith(item) в этом случае '.'.join(map(str, item)) :
    1. item например (1, 11)
    2. map(str, item) преобразует каждый элемент, который у item нас есть, в строки: ["1", "11"] <- На самом деле не список, но похожий
    3. '.'.join() объединяет список строк с помощью ‘.’: "1.11"
  7. Итак, final_result это желаемый результат: ["0.1", "1.1.1", ...]