Python辞書のリストを特定キーの値でソートする方法完全ガイド:効率的な並び替えテクニック
Python開発では、辞書のリストを特定のキーの値に基づいてソートしたい場面が頻繁にあります。例えば、「ユーザーを年齢順に並べたい」「商品を価格の安い順に表示したい」といったデータの並び替え処理です。本記事では、辞書のリストを効率的にソートする様々な方法を、実践的なサンプルコードとともに詳しく解説します。
基本的なソート方法
sorted()関数とkey引数を使った基本的なソート
最もシンプルで推奨される方法です。
students = [
{'name': '太郎', 'age': 25, 'score': 85},
{'name': '花子', 'age': 22, 'score': 92},
{'name': '次郎', 'age': 28, 'score': 78}
]
# 年齢の昇順でソート
age_sorted = sorted(students, key=lambda x: x['age'])
print("年齢順:")
for student in age_sorted:
print(f"{student['name']}: {student['age']}歳")
# 花子: 22歳, 太郎: 25歳, 次郎: 28歳
# 得点の降順でソート
score_sorted = sorted(students, key=lambda x: x['score'], reverse=True)
print("得点順:")
for student in score_sorted:
print(f"{student['name']}: {student['score']}点")
# 花子: 92点, 太郎: 85点, 次郎: 78点
list.sort()メソッドを使った破壊的ソート
products = [
{'name': 'ノートPC', 'price': 80000, 'category': 'electronics'},
{'name': 'マウス', 'price': 2000, 'category': 'electronics'},
{'name': 'キーボード', 'price': 5000, 'category': 'electronics'}
]
# 元のリストを直接変更
products.sort(key=lambda x: x['price'])
print("価格の安い順:")
for product in products:
print(f"{product['name']}: {product['price']:,}円")
# マウス: 2,000円, キーボード: 5,000円, ノートPC: 80,000円
operator.itemgetter()を使った効率的なソート
from operator import itemgetter
employees = [
{'name': '田中', 'department': '営業', 'salary': 500000},
{'name': '佐藤', 'department': '開発', 'salary': 600000},
{'name': '鈴木', 'department': '総務', 'salary': 450000}
]
# itemgetterを使用(lambda関数より高速)
salary_sorted = sorted(employees, key=itemgetter('salary'), reverse=True)
print("給与の高い順:")
for emp in salary_sorted:
print(f"{emp['name']}: {emp['salary']:,}円")
# 佐藤: 600,000円, 田中: 500,000円, 鈴木: 450,000円
複数キーによるソート
複数条件でのソート
sales_data = [
{'region': '東京', 'month': 1, 'sales': 1000000},
{'region': '大阪', 'month': 1, 'sales': 800000},
{'region': '東京', 'month': 2, 'sales': 1200000},
{'region': '大阪', 'month': 2, 'sales': 900000}
]
# 地域順、その後月順でソート
multi_sorted = sorted(sales_data, key=lambda x: (x['region'], x['month']))
print("地域→月順:")
for data in multi_sorted:
print(f"{data['region']} {data['month']}月: {data['sales']:,}円")
# 売上降順、その後地域昇順
complex_sorted = sorted(sales_data, key=lambda x: (-x['sales'], x['region']))
print("売上降順→地域昇順:")
for data in complex_sorted:
print(f"{data['region']} {data['sales']:,}円")
itemgetterで複数キー指定
from operator import itemgetter
inventory = [
{'product': 'A', 'category': 'electronics', 'stock': 10, 'price': 1000},
{'product': 'B', 'category': 'books', 'stock': 5, 'price': 500},
{'product': 'C', 'category': 'electronics', 'stock': 15, 'price': 800}
]
# カテゴリ→価格の順でソート
category_price_sorted = sorted(inventory, key=itemgetter('category', 'price'))
print("カテゴリ→価格順:")
for item in category_price_sorted:
print(f"{item['category']} - {item['product']}: {item['price']}円")
異なるソート方向の組み合わせ
rankings = [
{'player': '太郎', 'score': 1500, 'time': 120},
{'player': '花子', 'score': 1500, 'time': 100}, # 同得点だが時間が短い
{'player': '次郎', 'score': 1200, 'time': 90}
]
# 得点降順、その後時間昇順(ゲームランキング風)
def game_sort_key(player):
return (-player['score'], player['time'])
game_sorted = sorted(rankings, key=game_sort_key)
print("ゲームランキング(得点高→時間短):")
for i, player in enumerate(game_sorted, 1):
print(f"{i}位: {player['player']} - {player['score']}点 ({player['time']}秒)")
# 1位: 花子 - 1500点 (100秒), 2位: 太郎 - 1500点 (120秒), 3位: 次郎 - 1200点 (90秒)
安全なソート処理
キーが存在しない場合の処理
incomplete_data = [
{'name': '太郎', 'age': 25, 'score': 85},
{'name': '花子', 'age': 22}, # scoreなし
{'name': '次郎', 'score': 78} # ageなし
]
# デフォルト値を使った安全なソート
def safe_sort_by_score(data):
return sorted(data, key=lambda x: x.get('score', 0), reverse=True)
safe_sorted = safe_sort_by_score(incomplete_data)
print("得点順(安全):")
for student in safe_sorted:
score = student.get('score', 'なし')
print(f"{student['name']}: {score}点")
# 太郎: 85点, 次郎: 78点, 花子: なし点
None値の処理
mixed_scores = [
{'name': '太郎', 'score': 85},
{'name': '花子', 'score': None},
{'name': '次郎', 'score': 78},
{'name': '美香', 'score': 0}
]
def handle_none_sort(data):
"""None値を最小値として扱う"""
def sort_key(item):
score = item.get('score')
return float('-inf') if score is None else score
return sorted(data, key=sort_key, reverse=True)
none_handled = handle_none_sort(mixed_scores)
print("None値考慮ソート:")
for student in none_handled:
score = student['score'] if student['score'] is not None else 'なし'
print(f"{student['name']}: {score}")
エラーハンドリング付きソート
risky_data = [
{'name': '太郎', 'value': 100},
{'name': '花子', 'value': '200'}, # 文字列
{'name': '次郎', 'value': [1, 2, 3]}, # リスト
{'name': '美香', 'value': 150}
]
def robust_sort(data, key_name, default_value=0):
"""エラーに強いソート"""
def safe_key(item):
try:
value = item.get(key_name, default_value)
# 数値に変換できるかチェック
if isinstance(value, str):
return float(value)
elif isinstance(value, (int, float)):
return value
else:
return default_value
except (ValueError, TypeError):
return default_value
return sorted(data, key=safe_key, reverse=True)
robust_sorted = robust_sort(risky_data, 'value')
print("エラー耐性ソート:")
for item in robust_sorted:
print(f"{item['name']}: {item['value']}")
実践的な応用例
日付によるソート
from datetime import datetime
events = [
{'title': 'ミーティング', 'date': '2024-03-15', 'priority': 'high'},
{'title': 'プレゼン', 'date': '2024-03-10', 'priority': 'urgent'},
{'title': '研修', 'date': '2024-03-20', 'priority': 'medium'}
]
# 日付文字列でソート
date_sorted = sorted(events, key=lambda x: x['date'])
print("日付順:")
for event in date_sorted:
print(f"{event['date']}: {event['title']}")
# datetimeオブジェクトに変換してソート
def parse_date_sort(events):
def date_key(event):
return datetime.strptime(event['date'], '%Y-%m-%d')
return sorted(events, key=date_key)
datetime_sorted = parse_date_sort(events)
print("datetime変換後:")
for event in datetime_sorted:
print(f"{event['date']}: {event['title']}")
カスタム順序によるソート
priority_data = [
{'task': 'タスクA', 'priority': 'medium'},
{'task': 'タスクB', 'priority': 'urgent'},
{'task': 'タスクC', 'priority': 'low'},
{'task': 'タスクD', 'priority': 'high'}
]
# 優先度のカスタム順序
priority_order = {'urgent': 1, 'high': 2, 'medium': 3, 'low': 4}
priority_sorted = sorted(priority_data, key=lambda x: priority_order.get(x['priority'], 999))
print("優先度順:")
for task in priority_sorted:
print(f"{task['priority']}: {task['task']}")
# urgent: タスクB, high: タスクD, medium: タスクA, low: タスクC
階層データのソート
employees = [
{'name': '田中', 'department': '営業', 'position': 'manager', 'salary': 600000},
{'name': '佐藤', 'department': '営業', 'position': 'staff', 'salary': 400000},
{'name': '鈴木', 'department': '開発', 'position': 'manager', 'salary': 650000},
{'name': '高橋', 'department': '開発', 'position': 'senior', 'salary': 550000}
]
# 部署→役職→給与の順でソート
position_rank = {'manager': 1, 'senior': 2, 'staff': 3}
hierarchical_sorted = sorted(
employees,
key=lambda x: (x['department'], position_rank.get(x['position'], 999), -x['salary'])
)
print("階層ソート(部署→役職→給与降順):")
for emp in hierarchical_sorted:
print(f"{emp['department']} - {emp['position']}: {emp['name']} ({emp['salary']:,}円)")
パフォーマンス最適化
大量データでの効率的ソート
import timeit
from operator import itemgetter
# テスト用大量データ
large_dataset = [
{'id': i, 'value': i % 1000, 'category': f'cat_{i % 10}'}
for i in range(10000)
]
# 方法1: lambda関数
def sort_with_lambda(data):
return sorted(data, key=lambda x: x['value'])
# 方法2: itemgetter
def sort_with_itemgetter(data):
return sorted(data, key=itemgetter('value'))
# 方法3: カスタム関数
def get_value(item):
return item['value']
def sort_with_function(data):
return sorted(data, key=get_value)
# パフォーマンステスト(小規模データで実行)
small_data = large_dataset[:1000]
time1 = timeit.timeit(lambda: sort_with_lambda(small_data), number=100)
time2 = timeit.timeit(lambda: sort_with_itemgetter(small_data), number=100)
time3 = timeit.timeit(lambda: sort_with_function(small_data), number=100)
print(f"lambda関数: {time1:.4f}秒")
print(f"itemgetter: {time2:.4f}秒") # 通常最高速
print(f"カスタム関数: {time3:.4f}秒")
メモリ効率的なソート
def memory_efficient_sort(dict_list, key_name, reverse=False, chunk_size=1000):
"""メモリ効率的なソート(大量データ向け)"""
# チャンク単位でソートしてマージ
import heapq
chunks = []
for i in range(0, len(dict_list), chunk_size):
chunk = dict_list[i:i + chunk_size]
chunk.sort(key=lambda x: x.get(key_name, 0), reverse=reverse)
chunks.append(chunk)
# マージソート的にチャンクを結合
result = []
while chunks:
if reverse:
# 最大値を取得
max_chunk_idx = max(range(len(chunks)),
key=lambda i: chunks[i][0].get(key_name, 0) if chunks[i] else float('-inf'))
result.append(chunks[max_chunk_idx].pop(0))
if not chunks[max_chunk_idx]:
chunks.pop(max_chunk_idx)
else:
# 最小値を取得
min_chunk_idx = min(range(len(chunks)),
key=lambda i: chunks[i][0].get(key_name, float('inf')) if chunks[i] else float('inf'))
result.append(chunks[min_chunk_idx].pop(0))
if not chunks[min_chunk_idx]:
chunks.pop(min_chunk_idx)
return result
# 使用例
test_data = [{'value': i} for i in [5, 2, 8, 1, 9, 3]]
sorted_data = memory_efficient_sort(test_data, 'value')
print("メモリ効率的ソート結果:")
for item in sorted_data:
print(item['value'])
実用的なソート関数
汎用ソートユーティリティ
class DictListSorter:
"""辞書リストソート用ユーティリティクラス"""
@staticmethod
def sort_by_key(dict_list, key, reverse=False, default_value=None):
"""単一キーでソート"""
return sorted(dict_list,
key=lambda x: x.get(key, default_value),
reverse=reverse)
@staticmethod
def sort_by_multiple_keys(dict_list, key_configs):
"""複数キーでソート
key_configs: [{'key': 'name', 'reverse': False}, {'key': 'age', 'reverse': True}]
"""
def multi_key(item):
return tuple(
item.get(config['key'], 0) * (-1 if config.get('reverse', False) else 1)
if isinstance(item.get(config['key'], 0), (int, float)) else item.get(config['key'], '')
for config in key_configs
)
return sorted(dict_list, key=multi_key)
@staticmethod
def sort_by_nested_key(dict_list, key_path, reverse=False, default_value=None):
"""ネストしたキーでソート"""
def nested_key(item):
current = item
try:
for key in key_path:
current = current[key]
return current
except (KeyError, TypeError):
return default_value
return sorted(dict_list, key=nested_key, reverse=reverse)
@classmethod
def smart_sort(cls, dict_list, sort_config):
"""設定に基づく柔軟なソート"""
if isinstance(sort_config, str):
# 単純なキー名
return cls.sort_by_key(dict_list, sort_config)
elif isinstance(sort_config, dict):
# 詳細設定
key = sort_config.get('key')
reverse = sort_config.get('reverse', False)
default = sort_config.get('default')
if isinstance(key, list):
# ネストしたキー
return cls.sort_by_nested_key(dict_list, key, reverse, default)
else:
# 単一キー
return cls.sort_by_key(dict_list, key, reverse, default)
elif isinstance(sort_config, list):
# 複数キー設定
return cls.sort_by_multiple_keys(dict_list, sort_config)
# 使用例
sorter = DictListSorter()
sample_data = [
{'name': '太郎', 'age': 25, 'profile': {'score': 85}},
{'name': '花子', 'age': 22, 'profile': {'score': 92}},
{'name': '次郎', 'age': 28, 'profile': {'score': 78}}
]
# 単純ソート
age_sorted = sorter.smart_sort(sample_data, 'age')
print("年齢順:", [p['name'] for p in age_sorted])
# ネストキーソート
score_sorted = sorter.smart_sort(sample_data, {
'key': ['profile', 'score'],
'reverse': True
})
print("得点順:", [p['name'] for p in score_sorted])
# 複数キーソート
multi_sorted = sorter.smart_sort(sample_data, [
{'key': 'age', 'reverse': False},
{'key': ['profile', 'score'], 'reverse': True}
])
print("年齢→得点順:", [p['name'] for p in multi_sorted])
まとめ
Python辞書のリストをソートする技術は、データ処理において非常に重要です。適切な方法を選択することで、効率的で保守性の高いコードが書けます。
基本的な手法
sorted()関数とlambda式が最も基本的itemgetter()は大量データで高速list.sort()は元リストを変更する破壊的ソート
高度なテクニック
- 複数キーでの段階的ソート
- カスタム順序による柔軟なソート
- エラーハンドリングによる安全なソート
パフォーマンスのポイント
- 大量データでは
itemgetter()を使用 - メモリ効率を考慮したチャンク処理
- 適切なデフォルト値設定でエラー回避
これらの技術を組み合わせることで、様々なデータソートタスクを効率的に処理できるでしょう。
■プロンプトだけでオリジナルアプリを開発・公開してみた!!
■AI時代の第一歩!「AI駆動開発コース」はじめました!
テックジム東京本校で先行開始。
■テックジム東京本校
「武田塾」のプログラミング版といえば「テックジム」。
講義動画なし、教科書なし。「進捗管理とコーチング」で効率学習。
より早く、より安く、しかも対面型のプログラミングスクールです。
<短期講習>5日で5万円の「Pythonミニキャンプ」開催中。
<月1開催>放送作家による映像ディレクター養成講座
<オンライン無料>ゼロから始めるPython爆速講座



