streamlitで「全てを選択」のチェックボックスの作成に苦労した話

## 目次
- はじめに
- streamlitとは
- 実装したい内容
- 苦労したことと解決方法
- コード
- まとめ

## はじめに

この投稿はアイスタイル [Advent Calendar 2023](https://qiita.com/advent-calendar/2023/istyle) の17日目の記事です。
こんにちは。アイスタイル新卒1年目のosadamです。
現在、研修を兼ねて、streamlitを用いてデータの可視化ツールの開発を行っています。
その中で、実装方法が分からず苦労した点があったので、記事にまとめました。

## streamlitとは
streamlitとは、簡単にwebアプリ開発が行えるpythonのフレームワークです。pythonだけで実装できてしまうので、フロントエンドの知識は特に必要ありません。
簡単に動かせるので、気になる方は、試してみてください。

https://streamlit.io/

## 実装したい内容

「全てを選択」のチェックボックスを作成し、以下のように挙動する。

![checkbox.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3591633/42232914-7aea-77a9-3088-e96509a75e31.gif)



「全てを選択」を押すと該当のチェックボックスが選択される。
「全てを選択」を押すと「全てを選択」のチェックボックスのみ選択が外れる。
個別のチェックボックスが押されている・押されていないによって「全て選択」のチェックボックスを選択したり、選択を外したりする。

## 苦労したことと解決方法
これを実装するにあたり、苦労した点は主に二つです。また、それぞれの解決方法についてもまとめていきます。

##### 1.全体像はイメージできているが、部分的にどういった処理をすればいいのか分からなくなってしまった
全体像は「実装したい内容」にも記載した通りイメージができていました。
しかし、いざそれを実装するとなった時に、具体的にどういった処理を書けばいいか分かっていませんでした。その結果、処理の仕方に誤りがあり、イメージした挙動になりませんでした。
解決策としては、実装したいことをそれぞれの部分で「日本語ベース」で記述することにしました。
```
全て選択を{True/False} にした場合
-Trueの場合
　選択ボタンをすべてTrueにする

選択ボタンがすべてTrueの場合
-全てをTrueにする

選択ボタンが一部True(False)、または全てFalseの場合
-全てをFalseにする
```
実は、実装したい内容はこれだけでした。
このように言葉で書くことでどういった処理があれば、全体として実装したいものになるのかを頭の中に落とし込むことができました。




##### 2.streamlitでどうやって実装すればいいのか分からなかった
さて、日本語に落とし込んでいざ実装しよう！と思ったのですが、ここでも躓いてしまいました。なぜなら、私は今まで実装の仕方が分からない際に、検索して出てきたコードをそのまま引用して使うことが多かったからです。
今までは、実装したいことと似たようなコードが検索で出てきたため、コードを読み込んだり、ライブラリのドキュメントを読んで理解を深めることができませんでした。
今回は、引用できるコードが見つからず、ライブラリのドキュメントを読んで使い方やその特性を理解することができませんでした。

この点については、今回は自分では解決できず業務メンターの方からいただいたサンプルコードを読み理解して実装しました。
自力で解決するには、今後経験を積んで考える力を付けていく必要があると思います。検索はとても便利ですが、検索に頼らず自分で考える力を付けていきたいです。

## コード

サンプルコードは以下になります。
```diff_python
import streamlit as st

#　ボタンの初期化(デフォルトは全てのチェックボックスが選択されている)
def init_buttons(all_button, part_button_list):
    if all_button not in st.session_state: 
        st.session_state[all_button] = True
    for part_button in part_button_list:
        if part_button not in st.session_state:
            st.session_state[part_button] = True


def changed_part_button_by_all_button(all_button, part_button_list):
    for part_button in part_button_list:
        if(st.session_state[all_button]):
            #全て選択をTrueにした場合、選択ボタンをすべてTrueにする
            st.session_state[part_button] = st.session_state[all_button]

def changed_all_button_by_part_button(all_button, part_button_list):
    if all(st.session_state[part_button] for part_button in part_button_list):
        # 選択ボタンがすべてTrueの場合、全てをTrueにする
        st.session_state[all_button] = True
    else:
        # 選択ボタンが一部True(False)または全てFalseの場合、全てをFalseにする
        st.session_state[all_button] = False

def create_checkbox_group(all_button, part_button_list, page_position):
    init_buttons(all_button, part_button_list)

    page_position.checkbox(
        all_button, 
        value=st.session_state[all_button], 
        key=all_button, 
        on_change=lambda: changed_part_button_by_all_button(all_button, part_button_list)
    )

    for part_button in part_button_list:
        page_position.checkbox(part_button, 
            value=st.session_state[part_button], 
            key=part_button, 
            on_change=lambda: changed_all_button_by_part_button(all_button, part_button_list)
        )

all_button = "全て選択"
part_button_list = ["a","b","c","d","e"]
create_checkbox_group(all_button, part_button_list, st)
```





## まとめ
今回は、実装したい挙動をどのようなアルゴリズムで組んでいくのかについて学んだことをまとめました。
実装したいものをすぐにコードにしようとするのではなく、言葉で処理の目的を整理することの大切さを学びました。言葉で書けないものは、コードでも書くのが難しいです。
まだまだ一人前には遠いですが、今後も考える力を少しずつでも付けていく努力をしていきます！