Pythonのリストから重複を削除する方法

【Python】リストから重複を削除する方法

公開: 更新:
CodeCampが提供するDX人材育成が可能なプログラミングやITが学べる公開講座

Pythonのリストから重複する要素を削除したいとき、「set()を使ったら元の順番が変わってしまった」「辞書を含むリストに対応できない」といった問題に直面することがあります。重複削除の手法は1つではなく、「順序を保持するか否か」と「リストの要素がハッシュ可能か否か」という2軸によって最適な方法が変わります。

間違った手法を選ぶと順番が崩れたり、実行時エラーが発生したりするため、状況に応じた選択が求められます。なお、この記事ではPythonの組み込みlist(他言語の配列に相当)を対象に解説します。

この記事では、Pythonのリストから重複を削除する代表的な手法を、コピペで動作するサンプルコードとともに紹介していきます。



Pythonのリスト(list)から重複を削除する方法

Pythonのリストから重複を削除する方法として、以下の3つが代表的です。なお、ここで紹介する3手法はいずれも数値・文字列などハッシュ可能な要素を対象としています。

  1. set()で重複を削除する
  2. dict.fromkeys()で順序を保持して重複を削除する
  3. forループとsetで順序を保持して重複を削除する

set()は最もシンプルですが、順序が保証されません。dict.fromkeys()とforループ+setはいずれも順序を維持したまま重複を除去できます。

用途に応じた選択基準として、「順序不要→set()」「順序保持かつシンプル→dict.fromkeys()」「条件付きフィルタリングも同時に行いたい→forループ+set」と覚えておきましょう。

set()で重複を削除する

set()はPythonの組み込み型で、重複を持たない集合(セット)を表します。リストをset()に渡すと重複が自動的に除去され、さらにlist()で包むことでリストに戻せます。

set()は内部でハッシュテーブルと呼ばれるデータ構造を使っています。ハッシュテーブルとは、各要素をハッシュ値(整数識別子)に変換して平均O(1)の計算量でアクセスできる仕組みのことです。

ただし、set()はハッシュ値に基づいて要素を管理するため、元のリストの並び順は保証されません。以下のコードで動作を確認してみましょう。

numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]

# set()で重複を除去してリストに変換
unique_numbers = list(set(numbers))

print(unique_numbers)
# 出力例: [9, 1, 2, 3, 4, 5, 6]  ※出力例のように順序が変わる場合がある

# 文字列リストも同様に使える
fruits = ["apple", "banana", "apple", "cherry", "banana"]
unique_fruits = list(set(fruits))

print(unique_fruits)
# 出力例: ['cherry', 'banana', 'apple']  ※順序は保証されない

元のリストとは異なる順番で要素が出力される場合があります。順番を気にせずユニークな値の集合を取り出すだけでよい場合に適した手法です。

順序を維持する必要があるときは、次のdict.fromkeys()を使う方法を選んでください。

dict.fromkeys()で順序を保持して重複を削除する

dict.fromkeys()は辞書クラスのクラスメソッドで、イテラブルのキーから辞書を生成します。Python 3.7以降、辞書は挿入順序を保持することが言語仕様として保証されており、この特性を利用して順序を維持したまま重複を除去できます。

dict.fromkeys(リスト)に渡すと各要素がキーとして登録され、重複するキーは最初の1件のみが保持されます。なお、各要素が辞書のキーとして使われるため、数値・文字列などハッシュ可能な要素のリストに限定されます。

辞書やリストなど unhashable な要素が含まれる場合はTypeErrorが発生するため、後述のJSON変換アプローチを使用してください。

以下のコードは、dict.fromkeys()で順序を維持しながら重複を削除する例です。

numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]

# dict.fromkeys()で順序を保持して重複を除去
unique_numbers = list(dict.fromkeys(numbers))

print(unique_numbers)
# 出力: [3, 1, 4, 5, 9, 2, 6]  ※元の順序を保持

# 文字列リストでも使える
fruits = ["apple", "banana", "apple", "cherry", "banana"]
unique_fruits = list(dict.fromkeys(fruits))

print(unique_fruits)
# 出力: ['apple', 'banana', 'cherry']  ※元の順序を保持

元のリストの順番通りに要素が出力される点が、set()との大きな違いです。Python 3.7未満の環境では辞書の挿入順序が言語仕様として保証されていないため、順序を維持する目的でのこの手法は信頼できません(CPython 3.6では実装上偶然保持されますが、言語仕様ではありません)。

現在のPythonプロジェクトでは3.7以降が一般的なため、順序を保ちながら重複削除する場面での標準的な選択肢として広く使われています。

the insertion-order preservation nature of dict objects has been declared to be an official part of the Python language spec.

出典:Python公式ドキュメント - What's New In Python 3.7

forループとsetで順序を保持して重複を削除する

forループとsetを組み合わせる手法では、「既に見た要素」を追跡するためのsetを用意し、set未登録の要素のみを新しいリストに追加していきます。setへの追加は平均O(1)で完了するため(要素がハッシュ可能な場合)、全体の処理は平均O(n)の計算量で実行されます。

dict.fromkeys()と同様に元の順序を保持でき、さらに条件付きフィルタリング(例: 特定の値を除外しながら重複削除)を同時に行いたい場合に柔軟に応用できる点が優位です。

以下のコードはforループ版(推奨)と、参考としてリスト内包表記版を示しています。内包表記版は副作用(setへの追加)を条件式に組み込むトリッキーな記法であり、チーム開発では可読性が低下するため通常は推奨されません。

numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]

# forループで明示的に順序を保ちながら重複を削除する(推奨)
seen = set()
unique_numbers = []
for x in numbers:
    if x not in seen:
        seen.add(x)
        unique_numbers.append(x)

print(unique_numbers)
# 出力: [3, 1, 4, 5, 9, 2, 6]  ※元の順序を保持

# 【参考】内包表記版(短く書けるが副作用を伴い可読性が落ちる)
seen2 = set()
# seen.add()はNoneを返すため、登録と条件判定を1行で行うテクニック
unique_numbers2 = [x for x in numbers if x not in seen2 and not seen2.add(x)]

print(unique_numbers2)
# 出力: [3, 1, 4, 5, 9, 2, 6]  ※同じ結果

forループ版は動作を明示的に記述しており、コードレビューや保守の場面で読みやすい実装です。条件付きフィルタリングを加える場合も、forループ版であればifを追記するだけで対応できます。


Python研修一覧はこちら

目的に合うPython研修を一覧形式から探したい方は、ぜひご利用ください。

Python研修を比較する

Java研修一覧はこちら

目的に合うJava研修を一覧形式から探したい方は、ぜひご利用ください。

Java研修を比較する

PHP研修一覧はこちら

目的に合うPHP研修を一覧形式から探したい方は、ぜひご利用ください。

PHP研修を比較する

新入社員研修

目的に合う新入社員研修を一覧形式から探したい方は、ぜひご利用ください。

新入社員研修を比較する

全ての研修からも探したい方はこちら

Pythonのリスト(配列)で重複をチェックする手順

Pythonのリストに重複する要素が含まれているかを調べる操作は、「重複の有無を判定する」と「どの要素が重複しているかを特定する」の2つに分けられます。用途に応じて適切な手法を選ぶことによって、コードの可読性と処理効率を両立させられます。

以下の2つの手順を順番に解説します。

  1. set()とlen()で重複の有無を判定する
  2. collections.Counterで重複する要素を特定する

1つ目は「重複が存在するかどうか」を真偽値で返すシンプルな判定で、2つ目は「どの要素が何回出現しているか」を詳しく調べる手法です。それぞれの特徴と使いどころを理解しておくと、実装の選択肢が広がります。

set()とlen()で重複の有無を判定する

set()への変換は平均O(n)のコストがかかります(nはリストの要素数)。変換前後の要素数をlen()で比較することによって、重複の有無を線形時間で判定できます。

「重複削除する必要があるか」を事前に確認したい場合に活用できます。

以下のコードは、リストに重複が含まれているかどうかをTrueまたはFalseで返す関数の実装例です。

def has_duplicates(lst):
    return len(lst) != len(set(lst))

# 使用例
fruits = ["apple", "banana", "apple", "cherry"]
numbers = [1, 2, 3, 4, 5]

print(has_duplicates(fruits))   # True
print(has_duplicates(numbers))  # False

len(lst)len(set(lst))の値が一致しない場合、重複が存在することを意味します。この手法は標準ライブラリのみで実装でき、コードが簡潔にまとまる点が優れています。

ただし、set()への変換はハッシュ可能な要素(数値や文字列、中身もハッシュ可能なタプルなど)にのみ対応しています。辞書やリストなどのミュータブルなオブジェクトはハッシュ不可のため、このアプローチではTypeErrorが発生します。

辞書やリストを含む場合の重複削除については「Pythonで辞書やリストを含むリストの重複を削除する方法」を参照してください。

collections.Counterで重複する要素を特定する

collections.Counterは、Pythonの標準ライブラリcollectionsモジュールに含まれるdictのサブクラスで、ハッシュ可能な要素の出現回数を集計します。

単に重複の有無を調べるだけではなく、「どの要素が重複しているか」「何回重複しているか」まで一度に把握できる点が、set()との大きな違いです。なお、辞書やリストなどハッシュ不可能な要素はCounter引数に渡せないため注意してください。

以下のコードは、Counterを使って重複する要素の一覧を取得する実装例です。most_common()メソッドを使うことによって、出現回数が多い順に要素を並べ替えて取得できます。

from collections import Counter

fruits = ["apple", "banana", "apple", "cherry", "banana", "apple"]

# 各要素の出現回数を集計
counter = Counter(fruits)
print(counter)
# Counter({'apple': 3, 'banana': 2, 'cherry': 1})

# 出現回数が2回以上の要素のみを抽出
duplicates = [item for item, count in counter.items() if count >= 2]
print(duplicates)
# ['apple', 'banana']

# 出現回数が多い順に上位2件を取得
most_common = counter.most_common(2)
print(most_common)
# [('apple', 3), ('banana', 2)]

Counter(fruits)を実行すると、各要素をキー、出現回数を値としたCounterオブジェクト(dictのサブクラス)が生成されます。この結果に対してitems()でキーと値のペアを取り出し、リスト内包表記で出現回数が2以上の要素を絞り込むことによって、重複要素の一覧を取得できます。

most_common(n)は出現回数が多い上位n件を返すメソッドで、頻出ワードの抽出やログ解析など、重複の多寡を分析したい場面で活用できます。単純な有無の判定にはset()を、要素ごとの出現回数や頻度順の取得が必要な場面にはCounterを選択するとよいでしょう。

Pythonで辞書やリストを含むリストの重複を削除する方法

辞書(dict)やリスト(list)などのミュータブル(変更可能)なオブジェクトを要素として含むリストでは、set()による重複削除が使えません。ハッシュ可能(hashable)とは、「生存期間中変わらないハッシュ値を持ち、他のオブジェクトと比較可能で、等しいオブジェクトは同じハッシュ値を持つ」ことを意味します(Python公式Glossaryの定義)。

辞書やリストはこの条件を満たさないため、set()に渡そうとするとTypeError: unhashable typeエラーが発生します。

ここでは、ハッシュ不可能な要素を含むリストの重複を削除するための以下の2つのアプローチを紹介します。

  1. JSON文字列に変換して重複を削除する
  2. 特定のキーを基準に辞書リストの重複を削除する

1つ目はJSON互換のデータ型全般に対応できる汎用的な手法で、2つ目は特定キーで重複を判定したい場合に適した手法です。それぞれの特性と計算量も合わせて確認しておきましょう。

それでは各手法について、詳しく解説していきます。

JSON文字列に変換して重複を削除する

シリアライズとは、オブジェクトをバイト列や文字列などの保存・転送可能な形式に変換する処理のことです。json.dumps()を使って辞書やリストをJSON形式の文字列(文字列はハッシュ可能)にシリアライズし、set()で重複を除去してから元のデータを保持するパターンです。

この手法はJSONで表現できるデータ型(辞書・リスト・文字列・数値・ブール値・None)に限定される点に注意してください。

キーの順序が異なる辞書(例: {"a": 1, "b": 2}{"b": 2, "a": 1})を同一内容として扱いたい場合は、sort_keys=Trueを指定してください。これによりキーがソートされた順で並び替えられ、同一内容の辞書が同じ文字列として扱われます。

なお、キー順序の違いを区別したい場合はsort_keys=Trueの指定は不要です。

import json

# 辞書を含むリスト(重複あり)
data = [
    {"id": 1, "name": "Alice"},
    {"id": 2, "name": "Bob"},
    {"id": 1, "name": "Alice"},  # 重複
    {"name": "Alice", "id": 1},  # キー順序違いだが同一内容
]

# json.dumps()でJSON文字列化 → setで重複除去 → 元データを順序保持で収集
seen = set()
result = []
for item in data:
    # sort_keys=Trueでキー順を統一してからシリアライズ
    serialized = json.dumps(item, sort_keys=True)
    if serialized not in seen:
        seen.add(serialized)
        result.append(item)

print(result)
# 出力: [{'id': 1, 'name': 'Alice'}, {'id': 2, 'name': 'Bob'}]

# ネストされたリストへの応用
nested = [[1, 2], [3, 4], [1, 2], [5, 6]]
seen2 = set()
result2 = []
for item in nested:
    serialized = json.dumps(item, sort_keys=True)
    if serialized not in seen2:
        seen2.add(serialized)
        result2.append(item)

print(result2)
# 出力: [[1, 2], [3, 4], [5, 6]]

上記コードではseenというセットにシリアライズ済み文字列を蓄積し、初めて登場した要素のみresultに追加しています。これにより挿入順序も保持されます。

この手法の計算量はO(n×k)です(nは要素数、kはJSON文字列長であり各辞書のフィールド数・値の長さに依存する変数)。setの検索はO(1)ですが、各要素をシリアライズする処理にO(k)かかります。

float('nan')float('inf')については、Pythonのjson.dumps()はデフォルト(allow_nan=True)でNaN・Infinity・-Infinityを非標準な文字列としてそのまま出力します(RFC 8259準拠ではありません)。

そのため、Python以外の言語や厳密なJSONパーサーに渡す場合はエラーになる可能性があります。

nan/infを厳密に弾きたい場合はallow_nan=Falseを指定するとValueErrorが発生します。NoneはJSONのnullとして正常に変換されるため問題ありません。

If sort_keys is true (default: False), then the output of dictionaries will be sorted by key.

出典:Python公式ドキュメント - json.dumps
項目 内容
計算量 O(n×k) ※kはJSON文字列長(フィールド数・値の長さに依存)
順序保持 保持する(挿入順)
対象データ型 JSON互換の型(辞書・リスト・文字列・数値・bool・None)に限定
注意点 キー順序を無視したい場合はsort_keys=True、nan/infはRFC非準拠(厳密に弾くにはallow_nan=False)、datetime・関数・集合は非対応

JSON変換を利用したこのパターンは、辞書とリストの両方に対応できる汎用性が特長です。ただし、関数やdatetimeオブジェクト、集合、循環参照を含む場合は使えないため、データの型を事前に確認しておきましょう。


Python基礎・実践(Django)

企業・法人向けのPython研修では、基礎から応用まで体系的に学べます。

Python研修の詳細

DX社員研修

企業・法人向けのDX研修では、実務に繋がるリスキリングでITレベルを向上させます。

DX研修の詳細

Javaエンジニア育成研修

企業・法人向けのJavaエンジニア育成研修では、Javaの基礎から応用まで確実に習得できます。

Java研修の詳細

新卒・新入社員向け研修

企業・法人に新入社員・新卒社員に向けたプログラミング研修を提供しています。

新入社員研修の詳細

コードキャンプのIT研修を全て見る

特定のキーを基準に辞書リストの重複を削除する

辞書のリストで「特定のキーの値が同じ要素を重複とみなして削除する」場合には、全フィールドの一致ではなく特定キーのみを基準にする手法が実用的です。例えばAPIレスポンスで"id"フィールドが一致する辞書を重複と判定するケースで活用できます。

なお、特定キーの値がハッシュ可能な型(文字列・整数など)である場合に使用できます。キー値に辞書やリストが含まれる場合はJSON変換アプローチを使用してください。

特定のキーの値をseenセットに追加するシンプルな実装で対処できます。参考として、辞書全体をタプルに変換してハッシュ化するアプローチ(全フィールドの完全一致で重複を判定する手法)も後述します。

このタプル変換は値もハッシュ可能な浅い辞書(値に辞書やリストを含まない場合)にのみ使用できる点に注意してください。

# 特定のキー("id")を基準に重複を削除する
data = [
    {"id": 1, "name": "Alice", "score": 90},
    {"id": 2, "name": "Bob",   "score": 80},
    {"id": 1, "name": "Alice", "score": 95},  # id=1の重複(scoreが違っても重複扱い)
    {"id": 3, "name": "Carol", "score": 70},
]

seen_ids = set()
result = []
for item in data:
    key = item["id"]
    if key not in seen_ids:
        seen_ids.add(key)
        result.append(item)

print(result)
# 出力:
# [{'id': 1, 'name': 'Alice', 'score': 90},
#  {'id': 2, 'name': 'Bob',   'score': 80},
#  {'id': 3, 'name': 'Carol', 'score': 70}]


# 【参考】辞書全体をタプルに変換してハッシュ化するアプローチ(全フィールド完全一致の判定用)
# 値がハッシュ可能な浅い辞書に限定して使用可能
# sorted()は挿入順が異なる同一内容の辞書を同一視するために必要
data2 = [
    {"name": "Alice", "score": 90},
    {"name": "Bob",   "score": 80},
    {"name": "Alice", "score": 90},  # 完全に同一の重複
]

seen_tuples = set()
result2 = []
for item in data2:
    # items()をソートしてタプル化(ハッシュ可能になる)
    key = tuple(sorted(item.items()))
    if key not in seen_tuples:
        seen_tuples.add(key)
        result2.append(item)

print(result2)
# 出力: [{'name': 'Alice', 'score': 90}, {'name': 'Bob', 'score': 80}]

1つ目の実装では"id"のみを基準にするため、同じidを持つ別スコアの辞書は最初に出現したものが採用されます。2つ目のタプル変換アプローチでは辞書の全フィールドが完全一致するものを重複とみなします。

sorted()を使うことで挿入順が異なる同一内容の辞書も同一視できますが、混在キーを含む場合はソートでTypeErrorになる可能性があります。値にリストや辞書など unhashable なオブジェクトが含まれる場合もエラーになるため、その場合はJSON変換アプローチを使ってください。

手法 計算量 用途
特定キーで判定 O(n) IDなど識別子が明確な場合(キー値がハッシュ可能な型に限定)
タプル変換で全フィールド判定 O(n×m log m) ※mはキー数(sorted()のコスト) 全フィールド完全一致で重複とする場合(値がハッシュ可能な浅い辞書に限定)
JSON文字列変換 O(n×k) ネスト構造を含む汎用的な場合

ループ内でin listを使って重複チェックする実装も見られますが、リスト全体を毎回走査するためO(n²)の計算量となり、要素数が増えるにつれて処理速度が急激に低下します。

上記のようにsetを補助的に使って重複を管理することによって、多くの場合はO(n)またはO(n×m log m)での処理が実現可能です。データの特性に応じて適切な手法を選択してください。

Pythonのリスト重複削除に関するよくある質問

set()で重複を削除すると順序が変わるのはなぜですか?

set()は内部でハッシュテーブルという仕組みを使って要素を管理しており、各要素はハッシュ値に基づいたメモリ位置に格納されます。この構造は重複の有無を高速に判定するために最適化されており、挿入順序を保持する設計ではありません。

そのため、set()に変換した時点で要素の並び順は元のリストと異なる場合があります。順序を保ちながら重複を削除したい場合は、要素がハッシュ可能であればdict.fromkeys()を使うのが推奨です。

2次元リストの重複を削除するにはどうすればよいですか?

リストはハッシュ化できないため、set()に直接渡してもエラーになります。2次元リストの重複を削除するには、内側のリストをタプルに変換するか、JSON文字列にシリアライズしてハッシュ可能な形式に変換してから処理します。

詳しい実装方法は「Pythonで辞書やリストを含むリストの重複を削除する方法」で解説しているので、そちらを参照してください。

大量のデータで最も高速な重複削除の方法は何ですか?

順序を問わない場合、set()への変換が平均的に高速です。ハッシュテーブルによる平均O(1)の検索を活かし、リストのサイズに比例した線形時間で処理が完了します。

数百万件規模でPandasNumPyのデータ構造をすでに利用している場合は、drop_duplicates()numpy.unique()の活用も選択肢です。ただし、リストからの変換コストがあるため、小〜中規模データではset()の方が高速なケースが多いです。

※上記コンテンツの内容やソースコードはAIで確認・デバッグしておりますが、間違いやエラー、脆弱性などがある場合は、コメントよりご報告いただけますと幸いです。

ITやプログラミングに関するコラム


ITやプログラミングに関するニュース

ブログに戻る

コメントを残す

コメントは公開前に承認される必要があることにご注意ください。

企業・法人向けのIT・プログラミング・生成AI研修を探す、比較する - IT・プログラミングを知って学べるコネクトメディア CodeCampが提供するDX人材育成が可能なプログラミングやITが学べる公開講座 - IT・プログラミングを知って学べるコネクトメディア コードキャンプが提供する無料で学べるプログラミングスクール講座 - IT・プログラミングを知って学べるコネクトメディア コードキャンプDX人材育成研修 - IT・プログラミングを知って学べるコネクトメディア 配属3ヶ月で30%の生産性向上を実現するいよぎんコンピュータサービスの新人研修に迫る - IT・プログラミングを知って学べるコネクトメディア 金融業界の業務効率化を加速するニッセイアセットマネジメントの生成AI×GAS活用研修事例 - IT・プログラミングを知って学べるコネクトメディア 【製造業のDX人材育成事例】デジタル人材の即戦力化を実現する、日本ガイシ株式会社の異動者向オンボーディング研修 - ITやプログラミングを知って学べるコネクトメディア フューチャーアーキテクト株式会社が実現した新入社員向けIT研修プログラムでタスクフォース制度が主体的な学びと成長を生み出す - IT・プログラミングを知って学べるコネクトメディア コードキャンプDX人材育成研修 - IT・プログラミングを知って学べるコネクトメディア コードキャンプIT・プログラミング研修事例/【IT新入社員研修】オンラインとオフラインの最適バランスを実現したFutureOneの導入事例 - IT・プログラミングを知って学べるコネクトメディア コードキャンプIT・プログラミング研修事例/【新入社員研修】柔軟なハイブリッド型Java研修で実現した新卒20名の成長と成果|サークレイス株式会社 - ITやプログラミングを知って学べるコネクトメディア コードキャンプIT・プログラミング研修事例/現場により近いところにデジタルを根付かせるDX基礎講座研修|株式会社ブリヂストン - ITやプログラミングを知って学べるコネクトメディア コードキャンプIT・プログラミング研修事例/業務の効率化・DX推進に向けたIT人材育成への第一歩|株式会社カナエ - ITやプログラミングを知って学べるコネクトメディア 企業・法人向けのIT・プログラミング研修 - ITやプログラミングを知って学べるコネクトメディア

新着記事

対象者別で探す

子供(小学生・中学生・高校生)向け
プログラミング教室検索する

子供(小学生・中学生・高校生)がロボットやプログラミング言語を学ぶことができるオフラインからオンラインスクールを検索、比較することが可能です。

子供(小学生・中学生・高校生)
プログラミング教室検索する

ITやプログラムなどの
最新情報を検索する

日々、新しいITやプログラミング言語の情報が流れていきますが、特定の情報を時系列でニュースやコラムを確認することができます。

ITやプログラムなどの
最新情報を検索する