Python | 1 日 120 分 × 7 日アプリ学習:JSON保存アプリ(中級編)

Web APP Python
スポンサーリンク

5日目のゴール

5日目のテーマは
「JSON に保存されたデータを“賢く取り出して見せる”」 ことです。

ここまでであなたはすでに

JSON に複数データを保存できる
起動時に JSON から復元できる
追加・更新・削除ができる
役割ごとにコードを整理できる

というところまで来ています。

5日目では一歩進んで、

特定の条件で絞り込む(フィルタ)
キーワードで検索する
並び替えて表示する(ソート)

という「見せ方の工夫」を学びます。
ここができると、アプリが一気に“使えるツール”になります。


今日の題材:ToDo JSONアプリを「見やすく・探しやすく」する

どんな機能を足すのかイメージする

4日目までの ToDo アプリは、こうでした。

タスクを追加できる
タスク一覧を表示できる
完了状態を変更できる
タスクを削除できる
JSON に保存・復元できる

5日目では、ここに次の機能を足します。

完了タスクだけ表示する
未完了タスクだけ表示する
タイトルにキーワードを含むタスクだけ表示する
タイトル順に並び替えて表示する

「データを持っている」だけでなく
「必要な形で取り出して見せる」ことが、
アプリとしての気持ちよさにつながります。


前提のデータ構造をもう一度確認する

タスク1件とタスク一覧の形

タスク1件は、こういう辞書です。

task = {
    "title": "本を30分読む",
    "done": False
}
Python

複数タスクはリストで持ちます。

tasks = [
    {"title": "本を30分読む", "done": False},
    {"title": "メールを整理する", "done": True}
]
Python

JSON ファイルではこうなります。

[
  {
    "title": "本を30分読む",
    "done": false
  },
  {
    "title": "メールを整理する",
    "done": true
  }
]

5日目でやることは、
この tasks に対して「条件で絞る」「順番を変える」です。


フィルタの基本:条件に合うものだけを集める

完了タスクだけを取り出す関数

まずは「完了したタスクだけ」を取り出す関数を作ります。

def filter_done_tasks(tasks):
    result = []
    for task in tasks:
        if task["done"]:
            result.append(task)
    return result
Python

ここでやっていることは、とてもシンプルです。

空のリスト result を用意する
tasks を1件ずつ見る
task["done"] が True のものだけ result に追加する
最後に result を返す

このパターンが「フィルタの基本形」です。

未完了タスクだけにしたい場合は、条件を逆にします。

def filter_undone_tasks(tasks):
    result = []
    for task in tasks:
        if not task["done"]:
            result.append(task)
    return result
Python

ここで大事なのは、
元の tasks を書き換えずに、新しいリストを返している ことです。
これにより、「全体」と「絞り込み結果」を安全に使い分けられます。


キーワード検索:タイトルに含まれる文字で絞る

部分一致で探す関数を作る

次に、「タイトルにキーワードを含むタスクだけ」を取り出します。

def search_tasks_by_keyword(tasks, keyword):
    result = []
    for task in tasks:
        if keyword in task["title"]:
            result.append(task)
    return result
Python

例えば、キーワードが "本" のとき、
タイトル "本を30分読む" には "本" が含まれているので True になります。

ここでのポイントは、

完全一致ではなく「含まれているかどうか」を見ている
キーワードが短くても動く(「勉強」「メール」など)

というところです。

さらに一歩進めて、
大文字・小文字を区別しない検索にすることもできます。

def search_tasks_by_keyword_case_insensitive(tasks, keyword):
    result = []
    keyword_lower = keyword.lower()
    for task in tasks:
        if keyword_lower in task["title"].lower():
            result.append(task)
    return result
Python

ここでは、
キーワードもタイトルも .lower() で小文字に変換してから比較しています。
英語を含むタスク名を扱うときに便利です。


並び替え:タイトル順にソートする

ソートは「新しいリストを返す」スタイルで

今度は、「タイトル順に並び替えたタスク一覧」を作ります。

def sort_tasks_by_title(tasks):
    return sorted(tasks, key=lambda task: task["title"])
Python

ここでのポイントは、

sorted は元のリストを壊さず、新しいリストを返す
key に「何を基準に並べるか」を指定する

という2つです。

lambda task: task["title"]
「タスクのタイトルを並び替えの基準にする」という意味です。

もし、完了状態で並び替えたいなら、こうも書けます。

def sort_tasks_by_done(tasks):
    return sorted(tasks, key=lambda task: task["done"])
Python

False が先、True が後になるので、
「未完了 → 完了」の順に並びます。


表示関数を「何でも受け取れる」ようにしておく

フィルタ結果もソート結果も同じ関数で表示する

4日目で作った表示関数を思い出します。

def show_tasks(tasks):
    if not tasks:
        print("タスクはありません。")
        return

    print("=== タスク一覧 ===")
    for task in tasks:
        mark = "✔" if task["done"] else "✗"
        print(f"{mark} {task['title']}")
Python

この関数は、
「渡された tasks をそのまま表示する」だけです。

だからこそ、

全タスクを表示したいときは show_tasks(tasks)
完了だけ表示したいときは show_tasks(filter_done_tasks(tasks))
未完了だけ表示したいときは show_tasks(filter_undone_tasks(tasks))
キーワード検索結果を表示したいときは
show_tasks(search_tasks_by_keyword(tasks, keyword))
タイトル順に並べて表示したいときは
show_tasks(sort_tasks_by_title(tasks))

というふうに、
「絞り込み・並び替え」と「表示」を自由に組み合わせられます。

ここが、5日目で一番おいしいところです。


5日目のミニアプリ:フィルタ・検索・ソート付き ToDo JSON アプリ

全体コード(4日目の続きとして)

import json
import os

FILENAME = "tasks.json"

def load_tasks():
    if not os.path.exists(FILENAME):
        return []
    with open(FILENAME, "r", encoding="utf-8") as f:
        return json.load(f)

def save_tasks(tasks):
    with open(FILENAME, "w", encoding="utf-8") as f:
        json.dump(tasks, f, ensure_ascii=False, indent=2)

def add_task(tasks, title):
    task = {"title": title, "done": False}
    tasks.append(task)
    return True

def find_task(tasks, title):
    for task in tasks:
        if task["title"] == title:
            return task
    return None

def update_task(tasks, title, done):
    task = find_task(tasks, title)
    if task is None:
        return False
    task["done"] = done
    return True

def delete_task(tasks, title):
    for i, task in enumerate(tasks):
        if task["title"] == title:
            del tasks[i]
            return True
    return False

def filter_done_tasks(tasks):
    result = []
    for task in tasks:
        if task["done"]:
            result.append(task)
    return result

def filter_undone_tasks(tasks):
    result = []
    for task in tasks:
        if not task["done"]:
            result.append(task)
    return result

def search_tasks_by_keyword(tasks, keyword):
    result = []
    for task in tasks:
        if keyword in task["title"]:
            result.append(task)
    return result

def sort_tasks_by_title(tasks):
    return sorted(tasks, key=lambda task: task["title"])

def show_tasks(tasks):
    if not tasks:
        print("タスクはありません。")
        return

    print("=== タスク一覧 ===")
    for task in tasks:
        mark = "✔" if task["done"] else "✗"
        print(f"{mark} {task['title']}")

def main():
    tasks = load_tasks()
    print("JSONファイルから読み込みました。")

    while True:
        print("\n=== メニュー ===")
        print("1: タスク追加")
        print("2: 全タスク表示")
        print("3: 完了タスクだけ表示")
        print("4: 未完了タスクだけ表示")
        print("5: キーワードで検索して表示")
        print("6: タイトル順に並べて表示")
        print("7: 完了状態を変更")
        print("8: タスク削除")
        print("0: 終了")

        choice = input("番号を選んでください: ")

        if choice == "1":
            title = input("タスク名: ")
            add_task(tasks, title)
            save_tasks(tasks)
            print("追加しました。")

        elif choice == "2":
            show_tasks(tasks)

        elif choice == "3":
            done_tasks = filter_done_tasks(tasks)
            show_tasks(done_tasks)

        elif choice == "4":
            undone_tasks = filter_undone_tasks(tasks)
            show_tasks(undone_tasks)

        elif choice == "5":
            keyword = input("キーワード: ")
            result = search_tasks_by_keyword(tasks, keyword)
            show_tasks(result)

        elif choice == "6":
            sorted_tasks = sort_tasks_by_title(tasks)
            show_tasks(sorted_tasks)

        elif choice == "7":
            title = input("変更するタスク名: ")
            done_text = input("完了なら1、未完了なら0: ")
            done = (done_text == "1")
            if update_task(tasks, title, done):
                save_tasks(tasks)
                print("更新しました。")
            else:
                print("タスクが見つかりません。")

        elif choice == "8":
            title = input("削除するタスク名: ")
            if delete_task(tasks, title):
                save_tasks(tasks)
                print("削除しました。")
            else:
                print("タスクが見つかりません。")

        elif choice == "0":
            save_tasks(tasks)
            print("終了します。")
            break

        else:
            print("正しい番号を入力してください。")

if __name__ == "__main__":
    main()
Python

5日目で絶対に押さえてほしい本質

「データをどう持つか」から「どう見せるか」へ

今日の一番大事なポイントは、

JSON に保存されたデータを
Python の list / dict として扱い、
そこに対して「条件で絞る」「順番を変える」だけで
かなりリッチなアプリになる

という感覚です。

特に意識してほしいのは次の三つです。

フィルタ関数は「新しいリストを返すだけ」
表示関数は「渡されたリストをそのまま表示するだけ」
ソートは sorted を使って元データを壊さない

この三つを守ると、
コードはシンプルなまま、機能だけどんどん増やせます。

もし「優先度をつけたい」「締切日で並べたい」など
次のアイデアが浮かんできたら、それはもう
“データ設計ができる人”の思考になっています。
その続きも、一緒に組み立てていけます。

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