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

Web APP Python
スポンサーリンク

5日目のゴール

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

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

JSONにメモを保存できる
起動時にメモを復元できる
メモの追加・編集・削除・検索ができる
コードを役割ごとに整理できる

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

5日目では一歩進んで、

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

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


今日の題材:メモを「ただの一覧」から「探しやすい一覧」へ

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

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

メモを追加できる
メモ一覧を表示できる
メモを編集できる
メモを削除できる
メモを検索できる

5日目では、ここに次のような「見せ方」を足します。

新しい順・古い順に並べ替える
重要なメモだけを表示する
特定のキーワードを含むメモだけを表示する(検索の強化)

そのために、まずはメモのデータ構造を少しだけリッチにします。


メモのデータ構造を少しリッチにする

作成日時と重要フラグを持たせる

今までのメモは、こうでした。

{"text": "牛乳を買う"}
Python

これに、次の2つを追加します。

created_at(作成日時)
important(重要フラグ)

Pythonではこうなります。

memo = {
    "text": "牛乳を買う",
    "created_at": "2025-05-05 10:30",
    "important": False
}
Python

JSONではこうです。

{
  "text": "牛乳を買う",
  "created_at": "2025-05-05 10:30",
  "important": false
}

ここで大事なのは、
「メモ=ただの文字列」ではなく「情報のかたまり」 として扱い始めることです。


作成日時を自動で入れる

datetimeモジュールを使ってみる

作成日時は、ユーザーに入力させるのではなく、
アプリ側で自動的に入れるのが自然です。

from datetime import datetime

def add_memo(memos, text):
    if text == "":
        return False

    now = datetime.now()
    created_at = now.strftime("%Y-%m-%d %H:%M")

    memo = {
        "text": text,
        "created_at": created_at,
        "important": False
    }

    memos.append(memo)
    return True
Python

ここでの重要ポイントは二つです。

一つ目は、datetime.now() で「今この瞬間」の日時を取得していること。
二つ目は、strftime("%Y-%m-%d %H:%M")
「2025-05-05 10:30」のような文字列にしていることです。

この形式にしておくと、
後で「文字列のままソートしても、時間順になる」というメリットがあります。


重要フラグを切り替える機能を作る

True / False をトグルする

メモには important というフラグを持たせました。
これを切り替える機能を作ります。

def toggle_important(memos, index):
    if index < 0 or index >= len(memos):
        return False
    memo = memos[index]
    memo["important"] = not memo["important"]
    return True
Python

ここでのポイントは、
memo["important"] = not memo["important"] という一行です。

True なら False に、
False なら True に、
ワンタッチで切り替えられます。

ユーザー入力と組み合わせると、こうなります。

def input_and_toggle_important(memos):
    if not memos:
        print("重要フラグを変更できるメモがありません。")
        return

    show_memos(memos)
    num_text = input("重要フラグを切り替えるメモの番号を入力してください: ")

    if not num_text.isdigit():
        print("数字を入力してください。")
        return

    num = int(num_text)
    index = num - 1

    if toggle_important(memos, index):
        print("重要フラグを切り替えました。")
    else:
        print("その番号のメモはありません。")
Python

表示を「情報量のある形」に変える

重要フラグと作成日時を表示する

メモの構造がリッチになったので、
表示もそれに合わせて変えます。

def show_memos(memos):
    if not memos:
        print("メモはまだありません。")
        return

    print("=== メモ一覧 ===")
    for i, memo in enumerate(memos, start=1):
        mark = "★" if memo.get("important") else " "
        text = memo.get("text", "")
        created_at = memo.get("created_at", "日時不明")
        print(f"{i}. {mark} {text} ({created_at})")
Python

ここでの重要ポイントは三つです。

一つ目は、memo.get("important") を使っていること。
古いJSONに important がない場合でもエラーにならず、
False として扱われます。

二つ目は、重要なメモには "★" をつけていること。
これだけで、一覧の中で「目立つメモ」が一目で分かります。

三つ目は、created_at を一緒に表示していること。
「いつ書いたメモか」が分かるだけで、
メモの意味がぐっと立体的になります。


並び替え:新しい順・古い順にソートする

created_atをソートキーに使う

作成日時を文字列 "YYYY-MM-DD HH:MM" で持っているので、
そのままソートに使えます。

def sort_memos_by_created_at(memos, reverse=False):
    return sorted(
        memos,
        key=lambda memo: memo.get("created_at", ""),
        reverse=reverse
    )
Python

reverse=False なら古い順、
reverse=True なら新しい順になります。

使い方の例はこうです。

def show_memos_newest_first(memos):
    sorted_memos = sort_memos_by_created_at(memos, reverse=True)
    show_memos(sorted_memos)

def show_memos_oldest_first(memos):
    sorted_memos = sort_memos_by_created_at(memos, reverse=False)
    show_memos(sorted_memos)
Python

ここで大事なのは、
「ソートは新しいリストを返すだけ」 にしていることです。
元の memos を壊さないので、
「通常表示」と「並び替え表示」を安全に使い分けられます。


フィルタ:重要なメモだけを表示する

条件に合うものだけを集める

今度は、「重要なメモだけ」を取り出す関数を作ります。

def filter_important_memos(memos):
    result = []
    for memo in memos:
        if memo.get("important"):
            result.append(memo)
    return result
Python

これは、フィルタの基本形です。

空のリスト result を用意する
memos を1件ずつ見る
条件に合うものだけ result に追加する
最後に result を返す

これを表示と組み合わせると、こうなります。

def show_important_memos(memos):
    important_memos = filter_important_memos(memos)
    if not important_memos:
        print("重要なメモはありません。")
        return
    show_memos(important_memos)
Python

ここでのポイントは、
「フィルタ」と「表示」を分けている ことです。
この分け方をしておくと、
「重要かつ新しい順」などの組み合わせも簡単に作れます。


検索を少しだけ賢くする

大文字・小文字を区別しない検索

3日目で作った検索は、
if keyword in memo["text"]: というシンプルなものでした。

ここに「大文字・小文字を区別しない」という工夫を足します。

def search_memos(memos, keyword):
    result = []
    keyword_lower = keyword.lower()
    for memo in memos:
        text = memo.get("text", "")
        if keyword_lower in text.lower():
            result.append(memo)
    return result
Python

これで、
"Python" でも "python" でも同じようにヒットします。

表示と組み合わせると、こうです。

def input_and_search_memos(memos):
    keyword = input("検索キーワードを入力してください: ")
    if keyword == "":
        print("キーワードが空です。")
        return

    results = search_memos(memos, keyword)
    if not results:
        print("そのキーワードを含むメモはありません。")
        return

    print("=== 検索結果 ===")
    show_memos(results)
Python

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

全体像をまとめて見る

ここまでの内容を組み合わせると、
5日目のアプリはこんな形になります。

import json
import os
from datetime import datetime

FILENAME = "memos.json"

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

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

def add_memo(memos, text):
    if text == "":
        return False
    now = datetime.now()
    created_at = now.strftime("%Y-%m-%d %H:%M")
    memo = {
        "text": text,
        "created_at": created_at,
        "important": False
    }
    memos.append(memo)
    return True

def input_and_add_memo(memos):
    text = input("メモを入力してください: ")
    if add_memo(memos, text):
        print("メモを追加しました。")
    else:
        print("空のメモは追加しません。")

def toggle_important(memos, index):
    if index < 0 or index >= len(memos):
        return False
    memo = memos[index]
    memo["important"] = not memo.get("important", False)
    return True

def input_and_toggle_important(memos):
    if not memos:
        print("重要フラグを変更できるメモがありません。")
        return
    show_memos(memos)
    num_text = input("重要フラグを切り替えるメモの番号を入力してください: ")
    if not num_text.isdigit():
        print("数字を入力してください。")
        return
    num = int(num_text)
    index = num - 1
    if toggle_important(memos, index):
        print("重要フラグを切り替えました。")
    else:
        print("その番号のメモはありません。")

def filter_important_memos(memos):
    result = []
    for memo in memos:
        if memo.get("important"):
            result.append(memo)
    return result

def sort_memos_by_created_at(memos, reverse=False):
    return sorted(
        memos,
        key=lambda memo: memo.get("created_at", ""),
        reverse=reverse
    )

def search_memos(memos, keyword):
    result = []
    keyword_lower = keyword.lower()
    for memo in memos:
        text = memo.get("text", "")
        if keyword_lower in text.lower():
            result.append(memo)
    return result

def input_and_search_memos(memos):
    keyword = input("検索キーワードを入力してください: ")
    if keyword == "":
        print("キーワードが空です。")
        return
    results = search_memos(memos, keyword)
    if not results:
        print("そのキーワードを含むメモはありません。")
        return
    print("=== 検索結果 ===")
    show_memos(results)

def show_memos(memos):
    if not memos:
        print("メモはまだありません。")
        return
    print("=== メモ一覧 ===")
    for i, memo in enumerate(memos, start=1):
        mark = "★" if memo.get("important") else " "
        text = memo.get("text", "")
        created_at = memo.get("created_at", "日時不明")
        print(f"{i}. {mark} {text} ({created_at})")

def main():
    memos = load_memos()
    print("JSONファイルからメモを読み込みました。")

    while True:
        print("\n=== メニュー ===")
        print("1: メモを追加")
        print("2: メモ一覧(作成順)")
        print("3: メモ一覧(新しい順)")
        print("4: 重要なメモだけ表示")
        print("5: メモを検索")
        print("6: 重要フラグを切り替え")
        print("0: 終了")

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

        if choice == "1":
            input_and_add_memo(memos)
            save_memos(memos)

        elif choice == "2":
            show_memos(sort_memos_by_created_at(memos, reverse=False))

        elif choice == "3":
            show_memos(sort_memos_by_created_at(memos, reverse=True))

        elif choice == "4":
            important_memos = filter_important_memos(memos)
            if not important_memos:
                print("重要なメモはありません。")
            else:
                show_memos(important_memos)

        elif choice == "5":
            input_and_search_memos(memos)

        elif choice == "6":
            input_and_toggle_important(memos)
            save_memos(memos)

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

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

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

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

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

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

JSONに保存されたデータを
Pythonのlist / dictとして扱い、
そこに対して「条件で絞る」「順番を変える」だけで
アプリの使い勝手が大きく変わる

という感覚です。

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

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

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

「タグで絞りたい」「カテゴリごとに見たい」など
次のアイデアが浮かんできたら、それはもう
“データを設計している人の思考” になっています。
6日目・7日目は、その感覚をさらに育てていくステージです。

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