Сортировка объектов в Python
Допустим, у меня есть куча футболок, которые мне нужно рассортировать по цвету.
Все "футболки" хранятся в списке tshirts_lst в виде строк формата 'id;color;material'. Например: '001;white;cotton'
Если допустить, что цветов всего 3 (white, black, gray), то рассортировать "футболки" по цвету можно циклом и иф-елсе:
white_tshirt = []
black_tshirt = []
gray_tshirt = []
for t_shirt in tshirts_lst:
if 'white' in t_shirt:
white_tshirt.append(t_shirt)
elif 'black' in t_shirt:
black_tshirt.append(t_shirt)
elif 'gray' in t_shirt:
gray_tshirt.append(t_shirt)
(да, корректней было бы искать "цвет" более явно в сплитованных строках, но в данном случае это не суть)
Но... если мне каждый из "цветов" нужно рассортировать еще и по "материалу" - создавать еще девять списков и делать еще три цикла по три иф-елсе? А если заранее неизвестно количество и названия "цветов" и "материалов"?
Есть более изящное и масштабируемое решение подобной сортировки? Может, через классы или есть способ создавать новые списки/условия динамически?
Подскажите, плиз, в какую сторону гуглить, или может кто видел подобные решения. Заранее спасибо.
UPD: Возможно, я не совсем корректно использовал слова "сортировка", и правильней было бы написать "рассортировка". В общем, на выходе все одинаковые "футболки" должны быть в своем отдельном списке: список "белых хлопковых", список "черных хлопковых", список "белых синтетических" и т.д. Количество списков на выходе равно количество цветов умноженное на количество материалов.
Проблема в том, что количество цветов/материалов и их вариаций не всегда постоянное и может расширяться в будущем, поэтому просто городить стационарную конструкцию из циклов и иф-елсе (пусть и завернутые в функцию) не вариант - нужно что-то масштабируемое.
Ответы (6 шт):
Не думал использовать ООП? Создать список объектов футболок и сортировать их, как захочешь?
class Tshirt:
def __init__(self, id, color, material):
self.id = id
self.color = color
self.material = material
Можно вот так сортировать, с предварительным превращением списка в список списков
def transform(mas):
ret = []
for item in mas:
ret.append(item.split(';'))
return ret
listFut = transform(futbols)
sortList = sorted(listFut, key=lambda row: (row[1],row[2]))
for it in sortList:
print(it)
в row[x] указываются номера "полей для сортировки"
Преобразовать список в pandas.Dataframe и сортировать/группировать как угодно по каким угодно полям.
Так как в задании был апдейт, у меня в ответе тоже апдейт. Сначала код для сортировки (в смысле алгоритмического понятия "сортировка") одного списка по значениям:
import pandas as pd
t_shirts_lst = ['001;white;cotton',
'002;red;cotton',
'003;green;cotton',
'004;white;wool',
'005;white;cotton',
'006;red;wool',
'007;white;cotton',
'008;red;acrylic',
'009;green;cotton',
'010;white;wool',
'011;white;linen',
'012;red;wool'
]
t_shirts_df = pd.DataFrame(
[a.split(';') for a in t_shirts_lst],
columns=['id', 'color', 'material']
)
#можно сортировать любое количество столбцов
#с любым количеством значений в любом порядке
t_shirts_df.sort_values(
by=['color', 'material'],
inplace=True,
ascending=[False, True],
ignore_index=True
)
print(t_shirts_df)
Результат:
| | id | color | material |
| -- | --- | ------ | --------- |
|0 |001 |white |cotton |
|1 |005 |white |cotton |
|2 |007 |white |cotton |
|3 |011 |white |linen |
|4 |004 |white |wool |
|5 |010 |white |wool |
|6 |008 |red |acrylic |
|7 |002 |red |cotton |
|8 |006 |red |wool |
|9 |012 |red |wool |
|10 |003 |green |cotton |
|11 |009 |green |cotton |
Если нужно вернуть обратно в список:
t_shirts_lst = [a[0]+';'+a[1]+';'+a[2] for a in t_shirts_df.values.tolist()]
Ниже код для сортировки (в смысле "сортировка вещей по кучкам"). В результате получаем список списков с "разобранными вещами":
import pandas as pd
t_shirts_lst = ['001;white;cotton',
'002;red;cotton',
'003;green;cotton',
'004;white;wool',
'005;white;cotton',
'006;red;wool',
'007;white;cotton',
'008;red;acrylic',
'009;green;cotton',
'010;white;wool',
'011;white;linen',
'012;red;wool'
]
#конвертируем список в датафрейм
#сразу разбиваем значения по колонкам
t_shirts_df = pd.DataFrame(
[a.split(';') for a in t_shirts_lst],
columns=['id', 'color', 'material']
).set_index('id', drop=True)
#группируем по цветам и материалам
#на выходе имеем словарь, где ключ - кортеж (цвет, материал),
#а значение - список индексов соответствующих вещей
g = t_shirts_df.groupby(['color', 'material']).groups
#превращаем словарь в список списков
t_shirts_lst = [
[ind + ';' + k[0] + ';' + k[1] for ind in v]
for k, v in zip(g.keys(), g.values())
]
print(t_shirts_lst)
Можно предложить такой вариант. (там ещё генератор примера, я его не стал убирать, вдруг пригодится)
from itertools import groupby
from random import choice
def color_material(tshirt):
return tshirt.split(':', 1)[1]
tshirts_lst = ['{:03}:{}:{}'.format(i,
choice(['white', 'black', 'gray']),
choice(['cotton', 'silk', 'wool']))
for i in range(30)]
tshirts_sorted = sorted(tshirts_lst, key=color_material)
tshirts_grupped = groupby(tshirts_sorted, key=color_material)
for key, tshirts in tshirts_grupped:
print(list(tshirts))
Пока что вот такой рабочий вариант получился на основе предложенных решений.
from itertools import groupby
# Исходный список "футболок"
tshirts_lst = ['001;white;cotton', '002;white;poliestr', '003;black;cotton', '004;white;poliestr', '005;black;cotton', '006;black;poliestr', '007;gray;cotton', '008;gray;cotton']
def color(tshirt):
return tshirt.split(';')[1]
def material(t):
return t.split(';')[2]
tshirts_sorted = sorted(tshirts_lst, key=color)
tshirts_grupped = groupby(tshirts_sorted, key=color)
sorted_color = []
for key, tshirts in tshirts_grupped:
sorted_color.append(list(tshirts))
sorted_color_material = []
for t in sorted_color:
tshirts_sorted = sorted(t, key=material)
tshirts_grupped = groupby(tshirts_sorted, key=material)
for key, tshirts in tshirts_grupped:
sorted_color_material.append(list(tshirts))
for lst in sorted_color_material:
print(lst)
# На выходе
['003;black;cotton', '005;black;cotton']
['006;black;poliestr']
['007;gray;cotton', '008;gray;cotton']
['001;white;cotton']
['002;white;poliestr', '004;white;poliestr']
Думаю, можно решить эту задачу еще более лаконично через классы и объекты, но с ООП пока нет никакого опыта. Буду благодарен, если кто-то подскажет алгоритм действий через классы/объекты.
Это не сортировка, а распределение (по корзинам по ключу). Ключ - кортеж цвет-материал. Функция distribute принимает список и функцию-ключ, возвращает список корзин. Каждая корзина хранит все элементы списка с одинаковым ключом:
def distribute(lst, key):
d = {}
for item in lst:
d.setdefault(key(item), []).append(item)
return list(d.values())
tshirts_lst = [
'001;white;cotton', '002;white;poliestr', '003;black;cotton',
'004;white;poliestr', '005;black;cotton', '006;black;poliestr',
'007;gray;cotton', '008;gray;cotton'
]
def key(s):
return tuple(s.split(';')[1:])
print(*distribute(tshirts_lst, key), sep='\n')
$ python distribute.py ['001;white;cotton'] ['002;white;poliestr', '004;white;poliestr'] ['003;black;cotton', '005;black;cotton'] ['006;black;poliestr'] ['007;gray;cotton', '008;gray;cotton']
P.S. Как вы функцию key напишите, так distribute и будет распределять элементы. Ограничение языка - key должен возвращать кортежи, не списки.