Python(メダル獲得状況は)

python

今回は受講生(K氏)からとどいた今まさに旬のオリンピックのお題をコーディングしてみよう。さまざまな方法でアプローチできる楽しいお題だ。

問題

3つの国名を入力する。
次に、それぞれの国の、金メダル、銀メダル、銅メダルの獲得数を入力し、総メダル獲得数を表示する。
最後に、金メダル獲得数と、総メダル獲得数において、それぞれ上位の国から順に表示する。

実行例

1つ目の国名を入力してください>>中国
2つ目の国名を入力してください>>アメリカ
3つ目の国名を入力してください>>日本
中国のメダル獲得数を入力してください
金メダル>>32
銀メダル>>21
銅メダル>>16
総メダル獲得数は[69]です。

アメリカのメダル獲得数を入力してください
金メダル>>25
銀メダル>>29
銅メダル>>21
総メダル獲得数は[75]です。

日本のメダル獲得数を入力してください
金メダル>>20
銀メダル>>7
銅メダル>>11
総メダル獲得数は[38]です。

<金メダル獲得数の順位>
['中国', 'アメリカ', '日本']
<総メダル獲得数の順位>
['アメリカ', '中国', '日本']

Let’s challenge

データの処理に何を使っていくか・・・
この最初の方針によってコーディングの難しさが変わってきそうだ。

解答例

まず思いつくデータ構造は以下のようなdict(辞書)ではないだろうか?

data={
 "中国":[32,21,16],
 "アメリカ":[25,29,21],
 "日本":[20,7,11],
}
 

dictを用いてコーディングしてみる

data=dict()
medals=['金','銀','銅']
for i in range(3):
    name=input(f'{i+1}つ目の国名を入力してください>>')
    data[name]=[None]*3
for name in data:
    print(f'{name}のメダル獲得数を入力してください')
    for i,medal in enumerate(medals):
        data[name][i]=int(input(f'{medal}メダル>>'))
    print(f'総メダル獲得数は[{sum(data[name])}]です。\n')

print('<金メダル獲得数の順位>')
gold_medals=sorted(data.items(),key=lambda n:n[1][0],reverse=True)
print([ n[0]  for n in gold_medals])
print('<総メダル獲得数の順位>')
all_medals=sorted(data.items(),key=lambda n:sum(n[1]),reverse=True)
print([ n[0]  for n in gold_medals])

これはこれでいいのだが、dictを使うとソートの処理がちょっとトリッキーになってしまっている。

dictのソート

dictはそれ単体では並び替えができないので、キーとバリューがペアとなったタプルを作成し、それを並べ替えるという手法をとる。1ステップずつ見ていこう。

data={
 "中国":[32,21,16],
 "アメリカ":[25,29,21],
 "日本":[20,7,11],
}

print(data.items())

items()メソッドを使うことでdict_items型のリストが作成される。一つ一つの要素がキーとバリューのタプルであることがわかる。

dict_items([('中国', [32, 21, 16]), ('アメリカ', [25, 29, 21]), ('日本', [20, 7, 11])])

これをsorted関数を使って並び替えよう。今回はタプルの2つ目の要素の1つ目(金メダル)の数で並べ替えてみる。

data={
 "中国":[32,21,16],
 "アメリカ":[25,29,21],
 "日本":[20,7,11],
}
items=data.items()
gold_medals=sorted(items,key=lambda item:item[1][0])
print(gold_medals)
[('日本', [20, 7, 11]), ('アメリカ', [25, 29, 21]), ('中国', [32, 21, 16])]

sortedによって金メダル昇順にタプルが並び替えられたリストが作成された。sortedに名前付き引数でkeyを指定して「何の値で並び替えるか?」を指定することができる。これはよく出てくるので覚えておいたほうがよい。lambda式を使ってitem[1][0]つまり金メダルを指定した。
もし、金メダル降順に並べたいのであれば以下のようにする

gold_medals=sorted(items,key=lambda item:item[1][0],reverse=True)

または

gold_medals=sorted(items,key=lambda item:-item[1][0])

これで並び替えを実現できた。
あとは指定のように国名だけとりだしたい

print([ n[0] for n in gold_medals])

いつものように内包表記を使って名前だけ取り出したリストを作成し、printに渡せば指定された出力が実現できる

クラスを作ってみる

やってみてわかったが、dictは並べ替えが発生する事案には向いてなさそうだ。
素直にclassを作ってみるのはどうだろうか?やってみよう。

colors=['金','銀','銅']
countries=[]
class Country:
    def __init__(self,name):
        self.name=name
    def set_medals(self,medals):
        self.medals=medals
for i in range(3):
    name=input(f'{i+1}つ目の国名を入力してください>>')
    countries.append(Country(name))
for country in countries:
    print(f'{country.name}のメダル獲得数を入力してください')
    medals=[]
    for color in colors:
        medals.append(int(input(f'{color}メダル>>')))
    country.set_medals(medals)
    print(f'総メダル獲得数は[{sum(country.medals)}]です。\n')
print('<金メダル獲得数の順位>')
countries.sort(key=lambda c : c.medals[0], reverse=True)
print([country.name for country in countries])
print('<総メダル獲得数の順位>')
countries.sort(key=lambda c : sum(c.medals), reverse=True)
print([country.name for country in countries])

Countryクラスを作成して、そのインスタンスをリストにつめる形式にしてみる。
これならば、直接リストをkey指定して並び替えることができるので先程よりかはわかりやすくなっている。
ただ、この程度の処理にクラスは少々大げさすぎるかもしれない。

2次元配列

この処理は結局2次元リストでやってしまうのが一番簡単かもしれない。

[
    ['中国',10,2,4],
    ['日本',8,4,6],
]

やってみよう。

data=[None]*3
medals=['金','銀','銅']
for i in range(len(data)):
    name=input(f'{i+1}つ目の国名を入力してください>>')
    data[i]=[name]
for c in data:
    print(f'{c[0]}のメダル獲得数を入力してください')
    for medal in medals:
        c.append(int(input(f'{medal}メダル>>')))
    print(f'総メダル獲得数は[{sum(c[1:])}]です。\n')
print('<金メダル獲得数の順位>')
data.sort(key=lambda c:c[1],reverse=True)
print([d[0] for d in data])
print('<総メダル獲得数の順位>')
data.sort(key=lambda c:sum(c[1:]),reverse=True)
print([d[0] for d in data])

やはり一番簡単にコーディングできた。話がもっと複雑になってきた場合はクラスでおこなったほうがよさそうだが、今回のお題を実現するだけだったら2次元配列がベストだったようだ。

仕上げ

方針が決まったのでもう少しスリムにしてみよう。

C_NUM=3
data=[[input(f'{i+1}つ目の国名を入力してください>>')] for i in range(C_NUM)]
for c in data:
    print(f'{c[0]}のメダル獲得数を入力してください')
    for medal in ['金','銀','銅']:
        c.append(int(input(f'{medal}メダル>>')))
    print(f'総メダル獲得数は[{sum(c[1:])}]です。\n')
for i,label in enumerate(['金','総']):
    print(f'<{label}メダル獲得数の順位>')
    data.sort(key=lambda c:c[1] if i==0 else sum(c[1:]),reverse=True)
    print([d[0] for d in data])

かなりスッキリとコーディングすることができた。
今回のように色々な方法で処理を実現してみると、それぞれの方法の得手不得手がわかりコーディング力も上がっていくだろう。

python
スポンサーリンク
シェアする
mjpurinをフォローする
ジョイタスネット

コメント

タイトルとURLをコピーしました