Python辞書のリストから特定キーの値リストを取得する方法完全ガイド:効率的な抽出・変換テクニック
Python開発では、辞書のリストから特定のキーの値だけを抽出してリスト化したい場面が頻繁にあります。例えば、「全ユーザーの名前リストが欲しい」「商品の価格一覧を作成したい」といったデータ処理です。本記事では、辞書のリストから特定キーの値を効率的に抽出する様々な方法を、実践的なサンプルコードとともに詳しく解説します。
基本的な値の抽出方法
リスト内包表記を使った基本的な抽出
最もシンプルで推奨される方法です。
users = [
{'name': '太郎', 'age': 25, 'city': '東京'},
{'name': '花子', 'age': 30, 'city': '大阪'},
{'name': '次郎', 'age': 28, 'city': '名古屋'}
]
# 名前のリストを取得
names = [user['name'] for user in users]
print(names) # ['太郎', '花子', '次郎']
# 年齢のリストを取得
ages = [user['age'] for user in users]
print(ages) # [25, 30, 28]
for文を使った抽出
products = [
{'name': 'ノートPC', 'price': 80000, 'category': 'electronics'},
{'name': 'マウス', 'price': 2000, 'category': 'electronics'},
{'name': '本', 'price': 1500, 'category': 'books'}
]
# for文で価格を抽出
prices = []
for product in products:
prices.append(product['price'])
print(prices) # [80000, 2000, 1500]
map()関数を使った抽出
students = [
{'name': '太郎', 'score': 85},
{'name': '花子', 'score': 92},
{'name': '次郎', 'score': 78}
]
# map()を使用した抽出
scores = list(map(lambda x: x['score'], students))
print(scores) # [85, 92, 78]
# operator.itemgetterを使用(より効率的)
from operator import itemgetter
scores2 = list(map(itemgetter('score'), students))
print(scores2) # [85, 92, 78]
安全な値の抽出(キー不存在への対処)
get()メソッドを使った安全な抽出
incomplete_data = [
{'name': '太郎', 'age': 25, 'email': 'taro@example.com'},
{'name': '花子', 'age': 30}, # emailなし
{'name': '次郎', 'email': 'jiro@example.com'} # ageなし
]
# デフォルト値を指定して安全に抽出
emails = [user.get('email', '未設定') for user in incomplete_data]
print(emails) # ['taro@example.com', '未設定', 'jiro@example.com']
ages = [user.get('age', 0) for user in incomplete_data]
print(ages) # [25, 30, 0]
条件付きの抽出
mixed_data = [
{'name': '太郎', 'age': 25},
{'name': '花子', 'age': 30},
{'name': '次郎'}, # ageキーなし
{'name': '美香', 'age': None} # age値がNone
]
# 存在するageのみ抽出
valid_ages = [user['age'] for user in mixed_data if 'age' in user and user['age'] is not None]
print(valid_ages) # [25, 30]
# 存在チェック付きの安全な抽出
def safe_extract(dict_list, key, default=None):
"""安全なキー値抽出"""
return [item.get(key, default) for item in dict_list if isinstance(item, dict)]
ages = safe_extract(mixed_data, 'age', 0)
print(ages) # [25, 30, 0, 0]
try-except文を使った抽出
risky_data = [
{'name': '太郎', 'score': 85},
{'name': '花子', 'score': '92'}, # 文字列
{'name': '次郎'}, # scoreキーなし
]
def extract_with_error_handling(dict_list, key):
"""エラーハンドリング付きの抽出"""
results = []
errors = []
for i, item in enumerate(dict_list):
try:
value = item[key]
results.append(value)
except KeyError:
errors.append(f"インデックス {i}: キー '{key}' が存在しません")
except Exception as e:
errors.append(f"インデックス {i}: エラー {e}")
return results, errors
scores, error_list = extract_with_error_handling(risky_data, 'score')
print(f"抽出成功: {scores}") # [85, '92']
print(f"エラー: {error_list}") # ["インデックス 2: キー 'score' が存在しません"]
複数キーの同時抽出
複数の値を同時に抽出
employee_data = [
{'name': '田中太郎', 'department': '営業', 'salary': 500000},
{'name': '佐藤花子', 'department': '開発', 'salary': 600000},
{'name': '鈴木次郎', 'department': '総務', 'salary': 450000}
]
# 複数キーを同時抽出
def extract_multiple_keys(dict_list, keys):
"""複数キーの値を同時抽出"""
return [[item.get(key) for key in keys] for item in dict_list]
names_and_salaries = extract_multiple_keys(employee_data, ['name', 'salary'])
print(names_and_salaries) # [['田中太郎', 500000], ['佐藤花子', 600000], ['鈴木次郎', 450000]]
# 辞書形式で抽出
def extract_as_dict(dict_list, keys):
"""指定キーのみの辞書リストを作成"""
return [{key: item.get(key) for key in keys} for item in dict_list]
filtered = extract_as_dict(employee_data, ['name', 'salary'])
print(filtered) # [{'name': '田中太郎', 'salary': 500000}, ...]
タプルでの同時抽出
coordinates = [
{'x': 10, 'y': 20, 'z': 5},
{'x': 15, 'y': 25, 'z': 8},
{'x': 12, 'y': 18, 'z': 3}
]
# タプルで座標を抽出
coord_tuples = [(point['x'], point['y']) for point in coordinates]
print(coord_tuples) # [(10, 20), (15, 25), (12, 18)]
# namedtupleを使った構造化
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y', 'z'])
structured_points = [Point(p['x'], p['y'], p['z']) for p in coordinates]
print(structured_points[0].x) # 10
条件付き抽出とフィルタリング
条件を満たす値のみ抽出
sales_data = [
{'product': 'ノートPC', 'price': 80000, 'sold': 10},
{'product': 'マウス', 'price': 2000, 'sold': 50},
{'product': 'キーボード', 'price': 5000, 'sold': 30},
{'product': 'モニター', 'price': 25000, 'sold': 15}
]
# 価格が10000円以上の商品名のみ抽出
expensive_products = [
item['product'] for item in sales_data
if item.get('price', 0) >= 10000
]
print(expensive_products) # ['ノートPC', 'モニター']
# 売上数が20個以上の商品の価格抽出
popular_prices = [
item['price'] for item in sales_data
if item.get('sold', 0) >= 20
]
print(popular_prices) # [2000, 5000, 25000]
複合条件での抽出
student_records = [
{'name': '太郎', 'grade': 'A', 'attendance': 95, 'score': 88},
{'name': '花子', 'grade': 'B', 'attendance': 88, 'score': 92},
{'name': '次郎', 'grade': 'A', 'attendance': 92, 'score': 85},
{'name': '美香', 'grade': 'C', 'attendance': 75, 'score': 78}
]
# 成績Aで出席率90%以上の学生名
excellent_students = [
student['name'] for student in student_records
if student.get('grade') == 'A' and student.get('attendance', 0) >= 90
]
print(excellent_students) # ['太郎', '次郎']
# 条件に応じた値の変換
def categorize_performance(records):
"""成績に応じた分類"""
return [
'優秀' if record.get('score', 0) >= 90 else
'良好' if record.get('score', 0) >= 80 else
'要努力'
for record in records
]
performance_levels = categorize_performance(student_records)
print(performance_levels) # ['良好', '優秀', '良好', '要努力']
実践的な応用例
CSVデータの処理
csv_data = [
{'id': '001', 'name': '商品A', 'price': '1000', 'category': 'electronics'},
{'id': '002', 'name': '商品B', 'price': '2000', 'category': 'books'},
{'id': '003', 'name': '商品C', 'price': '1500', 'category': 'electronics'}
]
def process_csv_data(data):
"""CSV風データの処理"""
# 価格を数値に変換して抽出
prices = [int(item['price']) for item in data if item['price'].isdigit()]
# カテゴリ別の商品名
categories = {}
for item in data:
category = item.get('category', 'unknown')
if category not in categories:
categories[category] = []
categories[category].append(item['name'])
return {
'prices': prices,
'categories': categories,
'total_products': len(data)
}
result = process_csv_data(csv_data)
print(f"価格リスト: {result['prices']}") # [1000, 2000, 1500]
print(f"カテゴリ別: {result['categories']}") # {'electronics': ['商品A', '商品C'], 'books': ['商品B']}
APIレスポンスの処理
api_response = [
{
'user_id': 1,
'profile': {'name': '太郎', 'age': 25},
'posts': [{'title': 'Hello', 'likes': 10}]
},
{
'user_id': 2,
'profile': {'name': '花子', 'age': 30},
'posts': [{'title': 'World', 'likes': 15}, {'title': 'Python', 'likes': 25}]
}
]
def extract_nested_data(response_list):
"""ネストしたデータの抽出"""
# ユーザー名を抽出
user_names = [
user.get('profile', {}).get('name', '不明')
for user in response_list
]
# 全投稿のタイトルを抽出
all_titles = []
for user in response_list:
posts = user.get('posts', [])
titles = [post.get('title', '') for post in posts]
all_titles.extend(titles)
# 総いいね数を計算
total_likes = []
for user in response_list:
posts = user.get('posts', [])
user_likes = sum(post.get('likes', 0) for post in posts)
total_likes.append(user_likes)
return {
'user_names': user_names,
'all_titles': all_titles,
'total_likes': total_likes
}
extracted = extract_nested_data(api_response)
print(f"ユーザー名: {extracted['user_names']}") # ['太郎', '花子']
print(f"全タイトル: {extracted['all_titles']}") # ['Hello', 'World', 'Python']
print(f"いいね総数: {extracted['total_likes']}") # [10, 40]
データベースレコードの処理
db_records = [
{'id': 1, 'created_at': '2024-01-01', 'status': 'active', 'metadata': {'version': '1.0'}},
{'id': 2, 'created_at': '2024-01-02', 'status': 'inactive', 'metadata': {'version': '1.1'}},
{'id': 3, 'created_at': '2024-01-03', 'status': 'active', 'metadata': {'version': '1.0'}}
]
class DatabaseRecordProcessor:
"""データベースレコード処理クラス"""
@staticmethod
def extract_ids(records, status_filter=None):
"""IDを抽出(ステータスフィルタ付き)"""
if status_filter:
return [r['id'] for r in records if r.get('status') == status_filter]
return [r['id'] for r in records]
@staticmethod
def extract_dates(records):
"""作成日を抽出"""
return [r.get('created_at') for r in records if 'created_at' in r]
@staticmethod
def extract_versions(records):
"""バージョン情報を抽出"""
versions = []
for record in records:
metadata = record.get('metadata', {})
version = metadata.get('version', 'unknown')
versions.append(version)
return versions
@classmethod
def process_all(cls, records):
"""全処理を実行"""
return {
'active_ids': cls.extract_ids(records, 'active'),
'all_dates': cls.extract_dates(records),
'versions': cls.extract_versions(records)
}
processor = DatabaseRecordProcessor()
results = processor.process_all(db_records)
print(f"アクティブID: {results['active_ids']}") # [1, 3]
print(f"バージョン: {results['versions']}") # ['1.0', '1.1', '1.0']
パフォーマンス最適化
大量データでの効率的な処理
import timeit
from operator import itemgetter
# 大量データの準備(テスト用は小規模)
large_dataset = [{'id': i, 'value': i * 2, 'name': f'item_{i}'} for i in range(1000)]
# 方法1: リスト内包表記
def method1_list_comp(data):
return [item['value'] for item in data]
# 方法2: map() + itemgetter
def method2_map_itemgetter(data):
return list(map(itemgetter('value'), data))
# 方法3: map() + lambda
def method3_map_lambda(data):
return list(map(lambda x: x['value'], data))
# 方法4: for文
def method4_for_loop(data):
result = []
for item in data:
result.append(item['value'])
return result
# パフォーマンステスト
time1 = timeit.timeit(lambda: method1_list_comp(large_dataset), number=100)
time2 = timeit.timeit(lambda: method2_map_itemgetter(large_dataset), number=100)
time3 = timeit.timeit(lambda: method3_map_lambda(large_dataset), number=100)
time4 = timeit.timeit(lambda: method4_for_loop(large_dataset), number=100)
print(f"リスト内包表記: {time1:.4f}秒")
print(f"map + itemgetter: {time2:.4f}秒") # 通常最高速
print(f"map + lambda: {time3:.4f}秒")
print(f"for文: {time4:.4f}秒")
メモリ効率的な処理
def memory_efficient_extract(dict_list, key, chunk_size=1000):
"""メモリ効率的な値抽出"""
for i in range(0, len(dict_list), chunk_size):
chunk = dict_list[i:i + chunk_size]
yield from (item.get(key) for item in chunk if isinstance(item, dict))
# ジェネレータを使った処理
def process_large_dataset(data, key):
"""大量データの処理例"""
# ジェネレータとして値を抽出
value_generator = memory_efficient_extract(data, key)
# 必要に応じて処理
total = 0
count = 0
for value in value_generator:
if isinstance(value, (int, float)):
total += value
count += 1
return total / count if count > 0 else 0
# 使用例
test_data = [{'score': i} for i in range(1000)]
average = process_large_dataset(test_data, 'score')
print(f"平均値: {average}") # 499.5
エラーハンドリングと型安全性
型チェック付きの抽出
from typing import List, Dict, Any, Optional, Union
def type_safe_extract(
dict_list: List[Dict[str, Any]],
key: str,
expected_type: type = None,
default_value: Any = None
) -> List[Any]:
"""型安全な値抽出"""
results = []
errors = []
for i, item in enumerate(dict_list):
if not isinstance(item, dict):
errors.append(f"インデックス {i}: 辞書ではありません")
continue
value = item.get(key, default_value)
if expected_type and value is not None:
if not isinstance(value, expected_type):
errors.append(f"インデックス {i}: 型が不正 (期待: {expected_type.__name__}, 実際: {type(value).__name__})")
continue
results.append(value)
if errors:
print("警告:")
for error in errors:
print(f" {error}")
return results
# 使用例
mixed_data = [
{'name': '太郎', 'age': 25},
{'name': '花子', 'age': '30'}, # 文字列(型違い)
'invalid', # 辞書ではない
{'name': '次郎', 'age': 28}
]
safe_ages = type_safe_extract(mixed_data, 'age', expected_type=int, default_value=0)
print(f"安全な年齢抽出: {safe_ages}") # [25, 0, 28]
汎用的な抽出ユーティリティ
class DictListExtractor:
"""辞書リスト値抽出ユーティリティ"""
@staticmethod
def extract_simple(dict_list, key, default=None):
"""シンプルな値抽出"""
return [item.get(key, default) for item in dict_list if isinstance(item, dict)]
@staticmethod
def extract_with_filter(dict_list, key, condition=None, default=None):
"""条件付き値抽出"""
results = []
for item in dict_list:
if not isinstance(item, dict):
continue
value = item.get(key, default)
if condition is None or condition(value):
results.append(value)
return results
@staticmethod
def extract_multiple(dict_list, keys, as_dict=False):
"""複数キーの値抽出"""
if as_dict:
return [{k: item.get(k) for k in keys} for item in dict_list if isinstance(item, dict)]
else:
return [[item.get(k) for k in keys] for item in dict_list if isinstance(item, dict)]
@staticmethod
def extract_nested(dict_list, key_path, default=None):
"""ネストしたキーの値抽出"""
results = []
for item in dict_list:
if not isinstance(item, dict):
continue
current = item
try:
for key in key_path:
current = current[key]
results.append(current)
except (KeyError, TypeError):
results.append(default)
return results
# 使用例
extractor = DictListExtractor()
# ネストしたデータ
nested_data = [
{'user': {'profile': {'name': '太郎'}}, 'score': 85},
{'user': {'profile': {'name': '花子'}}, 'score': 92},
{'user': {}, 'score': 78} # profileキーなし
]
# ネストした値の抽出
names = extractor.extract_nested(nested_data, ['user', 'profile', 'name'], '不明')
print(f"ネストした名前: {names}") # ['太郎', '花子', '不明']
# 条件付き抽出
high_scores = extractor.extract_with_filter(nested_data, 'score', lambda x: x and x >= 90)
print(f"高得点: {high_scores}") # [92]
まとめ
Python辞書のリストから特定キーの値を抽出する技術は、データ処理において非常に重要です。適切な方法を選択することで、効率的で読みやすいコードが書けます。
基本的な手法
- リスト内包表記が最もシンプルで推奨される方法
map()
+itemgetter()
が大量データで最高速get()
メソッドでキー不存在に安全に対処
実用的なポイント
- エラーハンドリングを必ず実装
- 型チェックでバグを防止
- 大量データではパフォーマンスとメモリ効率を考慮
高度なテクニック
- ネストしたデータの安全な抽出
- 条件付きフィルタリング
- ジェネレータによるメモリ効率化
これらの技術を組み合わせることで、様々なデータ処理タスクを効率的かつ安全に実行できるでしょう。
■プロンプトだけでオリジナルアプリを開発・公開してみた!!
■AI時代の第一歩!「AI駆動開発コース」はじめました!
テックジム東京本校で先行開始。
■テックジム東京本校
「武田塾」のプログラミング版といえば「テックジム」。
講義動画なし、教科書なし。「進捗管理とコーチング」で効率学習。
より早く、より安く、しかも対面型のプログラミングスクールです。
<短期講習>5日で5万円の「Pythonミニキャンプ」開催中。
<月1開催>放送作家による映像ディレクター養成講座
<オンライン無料>ゼロから始めるPython爆速講座