Python 初学者が楽天APIを使って買い物リスト作成アプリを作る（材料の計算編）

**このページは以下の続きです**

https://qiita.com/yuuauuy1/items/6d399f6903d9cf068169

今回は、
選択したレシピの材料を自分が作りたい人数分の量に変換
⇩
同じ材料があれば足し算
⇩
お買い物リストとして表示

という部分を作っていきます。

**イメージはこちら**
選択したレシピの詳細を表示され、ユーザーが何人分の材料にするか入力する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983287/dde0dd6e-4201-6387-6177-dddec12ddb23.png)

材料がユーザーが入力した人数分に変換されて、表示される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983287/da3514b2-7fc0-62dc-191f-056ec7447614.png)

**買い物リスト作成アプリの概要はこちら**

https://qiita.com/yuuauuy1/items/5bb4a53ab15bde354297

**目次**

| 項番 | ページ内リンク | 
|:-:|:-:|
| 1 |[材料を計算する手順](#材料を計算する手順)|
| 2 |[ユーザーが選択したレシピを表示](#ユーザーが選択したレシピを表示)|
| 3 |[分数の計算](#分数の計算)|
| 4 |[辞書の足し算](#辞書の足し算)|
| 5 |[買い物リストとして表示](#買い物リストとして表示)|
| 6 |[楽しかった](#楽しかった)|

# 材料を計算する手順

1. ユーザーが選択したレシピを表示
2. ユーザーが欲しい人数分を入力する
3. （選択したレシピの材料の量）×　（ユーザーが欲しい人数分）/（選択したレシピが何人前の表記か）を計算
4. 選択したレシピの中に同じ材料があれば、量を足す
5. 材料を買い物リスト一覧として表示

この手順で分割してプログラムを考えていきます。


# ユーザーが選択したレシピを表示
選択したレシピは　index_list　というリストに入っています。

また、以下のレシピ一覧表は１行ずつがリストとして　index_name_listに格納されています。（※詳細は[前回の記事](https://qiita.com/yuuauuy1/items/6d399f6903d9cf068169)）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983287/22c2e7d7-0d46-a130-9671-857045d4d75f.png)

index_listとindex_name_listのindexが一致するものを探して、レシピ名やレシピURLを表示していきます。
さらに、
「１番目、レシピindex:●●、レシピ名:～～」
「２番目、レシピindex:●●、レシピ名:～～」

と、何番目のレシピか分かるようにします。
```python:Python
count = 1
for index in index_list: #選択したレシピのリスト
    for index_name in index_name_lists: #レシピ一覧表のリスト
        if int(index) == index_name[0] : 
            title = index_name[1] #レシピ名
            URL = index_name[2]
            number_of_people = index_name[3] #何人分のレシピか

            print("*********************************")
            print(str(count) + "番目, レシピindex : " +str(index)+ " , こちらは " +str(number_of_people) + " 人分のレシピです")
            print("レシピ名 「 "+ title+" 」")
            print("レシピURL : " + URL )
```

# ユーザーが欲しい人数分を入力する
input関数を使って、ユーザーにお買い物リストとして何人分の材料が必要か入力してもらいます。
そして、その数字が整数であるか前回の記事と同様に`isdigitメソッド`で判定します。
>参考文献
>[文字列の中の文字が数を表す文字かどうかを判定する(isdecimal, isdigit, isnumeric)](https://www.javadrive.jp/python/string/index14.html)

```python:Python
print("何人分の材料を買いたいですか？")
human_n = input("数字で入力してください")                             
human_n_result = human_n.isdigit() #ユーザーが作りたい人数が整数であるか判定
```
isdigitメソッドは、指定した文字列がすべて数字であるときにTrueを返します。
次は、Trueであった時のコードを書いていきます。

# 分数の計算
例えば、こちらのレシピ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983287/700ca52d-6f68-038f-7e41-a1d4f3ff9193.png)
もともと２人分に材料を、３人分に変換しようと思ったら、
各材料に ×3/2　をしなければなりません。

（選択したレシピの材料の量）×　（ユーザーが欲しい人数分）/（選択したレシピが何人前の表記か）　という計算になります。

牡蠣は、　180(g) ×　3/2 = 270(g) と整数になりますが、
しそは、　5(枚) × 3/2 = 15/2(枚)　と分数になります。

python で分数を扱うときは`fractionsモジュール`を使います。

以下のようにFractionに二つの数値型の引数を渡すか、
文字列型で直接分数を渡すことで表現できます。
```python:fractions
from fractions import Fraction
Fraction(16, -10)
>>>Fraction(-8, 5) #結果
Fraction(123)
>>>Fraction(123, 1)
Fraction()
>>>Fraction(0, 1)
Fraction('3/7')
>>>Fraction(3, 7)
Fraction(' -3/7 ')
>>>Fraction(-3, 7)
```
>参考文献
>[fractions --- 有理数(python公式ドキュメント)](https://docs.python.org/ja/3/library/fractions.html)

これを使うと
```python:Python
from fractions import Fraction
num = Fraction(index_ing[3]) #レシピに記載されている材料の量
new_amount = num * Fraction(int(human_n), int(number_of_people)) #欲しい人数の量に変換
```
こちらで、材料を欲しい人数分に変換することが出来ます。

この材料と変換した量を辞書にするコードと、条件分岐を追加し、関数化したコードはこちら
```python:Python
def choose_n_person():   
    count = 1
    dic_lists = []
    for index in index_list:    
        for index_name in index_name_lists:

            if int(index) == index_name[0] : 
                number_of_people = index_name[3] #何人分のレシピか
                title = index_name[1] #レシピの名前
                print("*********************************")
                print(str(count) + "番目, レシピNo : " +str(index)+ " , こちらは " +str(number_of_people) + " 人分のレシピです")
                print("レシピ名 「 "+ title+" 」")
                                
                def calc_fractions(): #分数の計算
                    print(word_list[10])#何人分の材料を買いたいですか？
                    human_n = input(word_list[9])  #ユーザーが作りたい人数を入力                           
                    human_n_result = human_n.isdigit() #ユーザーが作りたい人数が整数であるか判定
                               
                    if human_n_result: 
                        ingredients =[] #{材料:量}を作るために空のリスト用意
                        amount_list =[]   
                        for index_ing in index_ing_lists: #「レシピindex, 材料, 量, 量(数字のみ), 単位, 材料+単位」の表のリスト
        
                            if int(index) == index_ing[0]: #index_ing[0]=レシピindex
                                ingredients_a.append(index_ing[5]) #index_ing[5]=材料+単位
                                num = Fraction(index_ing[3]) #index_ing[3]=量(数字のみ)
                                new_num = num * Fraction(int(human_n), int(number_of_people)) #欲しい人数の量に変換
                                amount_list.append(new_num)
                                dic= dict(zip(ingredients, amount_list)) 

                        dic_lists.append(dic) # 辞書同士を連結させるために、辞書のリストを作る
                    else:
                        print("【！】 数字以外の文字が入力されました。")
                        calc_fractions()
                calc_fractions()
        count += 1
    return dic_lists
```
実行すると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983287/83a8ee58-8b10-9cb5-818f-76e6720047c6.png)
この様に、辞書のリストが返ってきます。こちらの例は３つ分のレシピの材料です。
例に使ったレシピは　こちら
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983287/cbcc5082-8eb0-1baa-989a-3512ae31feb5.png)
すべて４人前に材料を変換します


# 辞書の足し算
なぜ、材料と量を辞書型にしてみたかというと、
「材料：量」をkeyとvalueとして取り出せるので、足し算し易そうと判断して、辞書型にしてみました。
>参考文献
>[Pythonの辞書（dict）のforループ処理（keys, values, items）](https://note.nkmk.me/python-dict-keys-values-items/)

方針としては、以下の流れでやってみます。
1. 空の辞書を用意
1. 先ほどの辞書のリストから１レシピずつ材料の辞書型をひとつずつ取り出します。
2. 取り出した辞書の中で、前の辞書と同じ名前の材料があれば、量を足し算をして空の辞書に追加
3. 同じ名前が無ければ空の辞書にそのまま追加

辞書のkeyは　`辞書名.keys()`で取り出せます。
```python:Python
new_dic = {}
for dic in dic_lists:                      
    for key in dic.keys() :
```
これを実行すると以下の、keyが取り出せます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983287/c6393850-1fe0-034a-50bf-030b477ed93c.png)

この中で、　◯がついているkeyは同じkeyがあるので条件分岐でvalue(量)を足していきます。
また、`辞書名.get(key名)`でvalueを取得することが出来ます。
さらに、既存の辞書に対して　新たに 要素を追加する場合は`辞書名["追加したいkey名"] = 追加したいvalue`で追加することが出来ます。

これを使って関数化するとこちら
```python:Python
new_dic = {}
def get_ingredirnts(dic_lists):
    for dic in dic_lists:                      
        for key in dic.keys() : 

            if key in new_dic: # dicとnew_dicに同じkeyがある場合はvalueを足す
                num_1 = Fraction(new_dic.get(key) or 0) #分数の分量があるのでFraction型に変換する      
                num_2 = Fraction(dic.get(key) or 0) #量：Noneがある可能性あるので　「or 0」 を入力      
                sum = num_1 + num_2          
                new_dic[key] = sum  #要素追加              
            else: # 同じkeyがない場合は　dicのvalueをそのまま採用
                new_dic[key] = dic[key]
```
実行すると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983287/7bf4641b-5945-07b0-0eaa-15e79058b062.png)
同じ材料は足されています。

# 買い物リストとして表示
最後に今までの関数を実行して、買い物リストを表示します。

```python:Python
create_index_namelist()
create_index_inglist()
input_word()
lists = choose_n_person() #こちらの関数で辞書のlistをreturnするので、返り値を代入しておきます。
get_ingredirnts(lists) #返り値を代入
```
辞書型は`辞書.items()`で`(key, value)`のタプル型で　keyとvalueをそれぞれ取り出すことができます。
```python:Python
print("#----買い物リスト---") 
print("調味料は０と表示されます")
print("（）の中は単位です")
for key, value in new_dic.items(): 
    print(key + " : " + str(value))
```
結果はこちら
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983287/bb32a0cf-90e9-9842-a636-c52f6babfb2f.png)
餅を16/3個　
ってとてもとても分かりにくいですが、概ね５個くらい必要だなと思ってもらえたら良いです。

実は、`float`で分数を小数に直して、`math.ceil()`で小数点を切り上げる方法も考えました・・・。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983287/df9abef4-f3fd-fe18-26ba-adea46067f60.png)
そうすると、餅６個は良いのですが、白菜13/12個 が 変換されて2玉必要になってしまったり、
逆に多く買い過ぎてしまうことになるなと考え、あえて分数のままで表示しています。

>参考文献
>[Pythonで小数点以下を切り捨て・切り上げ: math.floor(), math.ceil()](https://note.nkmk.me/python-math-floor-ceil-int/)

# 楽しかった
以上で、材料の計算編終了です。
データを加工後、検索システム考えたり、どうやって同じ材料を足せるようにプログラムを書けばいいんだ・・・
と悩んだり、四苦八苦しましたがパズルを考えているようでかなり楽しかったです！

一番しんどかったのはデータ加工です・・・。
バックエンドのちょっとの部分しかできていないので、これからフレームワークやデータベースの勉強して、実用的なものにしていきたいです。