python key関数を用いたソートを行おう！(備忘録）

## sortメソッドについて
組み込み型にlistは、いろいろな基準でlistの要素を順序で整列するsortメソッドが存在する！

~~~python
# sort()を用いてlist内の要素を小さいものから大きいものに並び変える
numbers = [100, 38, 2, 13, 67]
numbers.sort()
print(numbers)



# 出力結果
>>>
[2, 13, 38, 67, 100]
~~~

sortメソッドは、文字列や、浮動小数点数などでは動作するが、オブジェクトでは動作しない。
~~~python
class stationary:
    def __init__(self, name, weight):
        self.name = name
        self.weight = weight

    def __repr__(self):
        return f'stationary({self.name!r}, {self.weight})'
    
various_stationery = [
    stationary('pen', 10),
    stationary('eraser', 20),
    stationary('notebook', 1000),
    stationary('wcissors', 32),
]

various_stationery.sort()


# 出力結果
>>>
TypeError: '<' not supported between instances of 'stationary' and 'stationary'
~~~    
クラスには、数字やアルファベットといった、自然に？並び替えられる順序がないため、並び替えができない。
そこで、オブジェクトで順序付けを行う。sortメッソドにkey関数を用いる。
＜f文字列中の「!r」について＞
⇒f文字列内の｛｝を評価する際に、呼び出すメソッドを定義するためのもの。[!r]でreprメソッド、[!s]でstrメッソド、[!a]でasciiメソッド。

## key関数を使おう
key関数を用いて名前順（アルファベット順）にソートしてみる
~~~python
class stationary:
    def __init__(self, name, weight):
        self.name = name
        self.weight = weight

    def __repr__(self):
        return f'stationary({self.name!r}, {self.weight})'
    
various_stationery = [
    stationary('pen', 10),
    stationary('eraser', 20),
    stationary('notebook', 1000),
    stationary('wcissors', 32),
]

print('Unsorted', repr(various_stationery))
print('*'*25)
various_stationery.sort(key=lambda x: x.name)
print('Sorted:', various_stationery)



# 出力結果
>>>
Unsorted [stationary('pen', 10), stationary('eraser', 20), stationary('notebook', 1000), stationary('wcissors', 32)]
*************************
Sorted: [stationary('eraser', 20), stationary('notebook', 1000), stationary('pen', 10), stationary('wcissors', 32)]
~~~

以下、f文字列の「!r」を外したバージョン
~~~python
# 出力結果
>>>
Unsorted [stationary(pen, 10), stationary(eraser, 20), stationary(notebook, 1000), stationary(wcissors, 32)]
*************************
Sorted: [stationary(eraser, 20), stationary(notebook, 1000), stationary(pen, 10), stationary(wcissors, 32)]
~~~
nameの部分にシングルクォーテーションがついてないことがわかります。

weight（重さ）でのソートも以下のように、できる
~~~python
print('Unsorted', repr(various_stationery))
print('*'*25)
various_stationery.sort(key=lambda x: x.weight)
print('Sorted:', various_stationery)


#　出力結果
>>>
Unsorted [stationary('pen', 10), stationary('eraser', 20), stationary('notebook', 1000), stationary('wcissors', 32)]
*************************
Sorted: [stationary('pen', 10), stationary('eraser', 20), stationary('wcissors', 32), stationary('notebook', 1000)]
~~~

## 複数の基準でソートする
先ほどの文房具を名前順や重さ順単体の基準でソートするだけでなく、名前順と重さ順2つの基準を持ったソートを行う。
    このような場合では、tuple型（タプル型)を用いる。タプルの比較は、左の要素から順々に比べられていく。

以下タプルの比べ方
~~~python
# 左から各要素で比べていく。「こんにちわ」の文字数が多いのでTrue
('おはよう', 6) < ('こんにちわ', 6)
>>>True

# 「いただきます」と「ごちそうさま」の文字数はともに6文字。次に「6」と「8」を比べて「8」が大きいのでTrue
('いただきます', 6) < ('ごちそうさま', 8)
>>>True
~~~

タプルの比較方式を用いて重さ順で並べ、さらに名前順で並べる

~~~python
class stationary:
    def __init__(self, name, weight):
        self.name = name
        self.weight = weight

    def __repr__(self):
        return f'stationary({self.name!r}, {self.weight})'
    
various_stationery = [
    stationary('pen', 10),
    stationary('eraser', 20),
    stationary('pencil_case', 1500),
    stationary('pencil_sharpener', 1000),
]


print('Unsorted', repr(various_stationery))
print('*'*25)
various_stationery.sort(key=lambda x: (x.weight, x.name))
print('Sorted:', various_stationery)

>>>
Unsorted [stationary('pen', 10), stationary('eraser', 20), stationary('pencil_case', 1500), stationary('pencil_sharpener', 1000)]
*************************
Sorted: [stationary('pen', 10), stationary('eraser', 20), stationary('pencil_sharpener', 1000), stationary('pencil_case', 1500)]
~~~

以下、名前順のみの場合
~~~python
print('Unsorted', repr(various_stationery))
print('*'*25)
various_stationery.sort(key=lambda x: x.name)
print('Sorted:', various_stationery)


>>>
Unsorted [stationary('pen', 10), stationary('eraser', 20), stationary('pencil_case', 1500), stationary('pencil_sharpener', 1000)]
*************************
Sorted: [stationary('eraser', 20), stationary('pen', 10), stationary('pencil_case', 1500), stationary('pencil_sharpener', 1000)]
~~~
比べてみると「pecil_case」と「pencil_sharpener」の順序が変わっていることがわかる。

## 昇順から降順にしてみよう
reverse=Trueを追加すると昇順から降順にできる。
~~~python
class stationary:
    def __init__(self, name, weight):
        self.name = name
        self.weight = weight

    def __repr__(self):
        return f'stationary({self.name!r}, {self.weight})'
    
various_stationery = [
    stationary('pen', 10),
    stationary('eraser', 20),
    stationary('pencil_case', 1500),
    stationary('pencil_sharpener', 1000),
]


print("Unsorted", repr(various_stationery))
print('*'*25)
various_stationery.sort(key=lambda x: (x.weight, x.name), reverse=True)
print("Sorted:", various_stationery)


>>>
Unsorted [stationary('pen', 10), stationary('eraser', 20), stationary('pencil_case', 1500), stationary('pencil_sharpener', 1000)]
*************************
Sorted: [stationary('pencil_case', 1500), stationary('pencil_sharpener', 1000), stationary('eraser', 20), stationary('pen', 10)]
~~~

## マイナス単項演算子をつけて昇順と降順の両立させる
reverse=Trueでは名前と重さ両方を逆にしたが「-」を用いることで、片方の要素に対して逆順にできる。
ちなみに、マイナス単項演算子はすべての型には使えず、nameに用いようとするとエラーが出力される。
~~~python
print("Unsorted", repr(various_stationery))
print('*'*25)
various_stationery.sort(key=lambda x: (-x.weight, x.name))
print("Sorted:", various_stationery)

>>>
Unsorted [stationary('pen', 10), stationary('eraser', 20), stationary('pencil_case', 1500), stationary('pencil_sharpener', 1000)]
*************************
Sorted: [stationary('pencil_case', 1500), stationary('pencil_sharpener', 1000), stationary('eraser', 20), stationary('pen', 10)]
~~~

## 大文字と小文字のソート
大文字と小文字をソートしようとすると、大文字⇒小文字の順で並び変えられる
~~~python
place = ['America', 'asia', 'Japan', 'jamaica']
place.sort()
print(place)

>>>
['America', 'Japan', 'asia', 'jamaica']
~~~

lowerメソッドを適用すると大文字小文字関係なくソートができるようになる。
~~~python
place = ['America', 'asia', 'Japan', 'jamaica']
place.sort(key=lambda x: x.lower())
print(place)

>>>
['America', 'asia', 'jamaica', 'Japan']
~~~