Como converter uma lista Python em um conjunto preservando a ordem original

PythonBeginner
Pratique Agora

Introdução

As estruturas de dados embutidas do Python oferecem maneiras flexíveis de gerenciar e manipular dados. Neste tutorial, exploraremos como converter uma lista Python em um conjunto (set), preservando a ordem original dos elementos. Essa técnica é particularmente útil quando você precisa remover duplicatas de uma lista, mas manter a ordem da primeira ocorrência de cada elemento único.

Ao final deste tutorial, você entenderá as diferenças entre listas e conjuntos (sets) em Python e aprenderá múltiplas técnicas para converter uma lista em um conjunto, mantendo a ordem original dos elementos.

Compreendendo Listas e Conjuntos (Sets) em Python

Antes de mergulharmos na conversão de listas em conjuntos, vamos entender as propriedades básicas dessas duas estruturas de dados em Python.

Listas Python

Listas em Python são coleções ordenadas que podem armazenar elementos de diferentes tipos de dados. Elas permitem valores duplicados e mantêm a ordem de inserção dos elementos.

Vamos criar um arquivo Python simples para demonstrar listas. Abra o editor de código e crie um novo arquivo chamado list_demo.py no diretório /home/labex/project:

## Listas em Python
my_list = [1, 2, 3, 2, 4, 5, 3]

print("Lista original:", my_list)
print("Comprimento da lista:", len(my_list))
print("Primeiro elemento:", my_list[0])
print("Último elemento:", my_list[-1])
print("Primeiros 3 elementos:", my_list[:3])
print("A lista contém duplicatas?", len(my_list) != len(set(my_list)))

Agora, execute este arquivo no terminal:

python3 list_demo.py

Você deve ver uma saída semelhante a esta:

Lista original: [1, 2, 3, 2, 4, 5, 3]
Comprimento da lista: 7
Primeiro elemento: 1
Último elemento: 3
Primeiros 3 elementos: [1, 2, 3]
A lista contém duplicatas? True

Conjuntos (Sets) Python

Conjuntos (Sets) são coleções desordenadas de elementos únicos. Quando você converte uma lista em um conjunto, elementos duplicados são removidos automaticamente, mas a ordem original não é preservada.

Vamos criar outro arquivo chamado set_demo.py para explorar conjuntos:

## Conjuntos em Python
my_list = [1, 2, 3, 2, 4, 5, 3]
my_set = set(my_list)

print("Lista original:", my_list)
print("Convertido para conjunto:", my_set)
print("Comprimento da lista:", len(my_list))
print("Comprimento do conjunto:", len(my_set))
print("O conjunto mantém a ordem?", list(my_set) == [1, 2, 3, 4, 5])

Execute este arquivo:

python3 set_demo.py

A saída mostrará:

Lista original: [1, 2, 3, 2, 4, 5, 3]
Convertido para conjunto: {1, 2, 3, 4, 5}
Comprimento da lista: 7
Comprimento do conjunto: 5
O conjunto mantém a ordem? False

Observe que o conjunto removeu todas as duplicatas, mas a ordem pode ser diferente da lista original. Isso ocorre porque os conjuntos em Python são inerentemente desordenados.

Abordagem Básica: Convertendo uma Lista em um Conjunto (Set)

Agora que entendemos as diferenças entre listas e conjuntos, vamos explorar como converter uma lista em um conjunto e as implicações dessa conversão.

Conversão Simples

A maneira mais básica de converter uma lista em um conjunto é usando a função embutida set(). Crie um novo arquivo chamado basic_conversion.py:

## Conversão básica de lista para conjunto
frutas = ["maçã", "banana", "laranja", "maçã", "pera", "banana"]

## Converte lista para conjunto (remove duplicatas, mas perde a ordem)
frutas_unicas = set(frutas)

print("Lista original:", frutas)
print("Como um conjunto:", frutas_unicas)

## Converte de volta para lista (ordem não preservada)
frutas_unicas_lista = list(frutas_unicas)
print("De volta para lista:", frutas_unicas_lista)

Execute este arquivo:

python3 basic_conversion.py

Você deve ver uma saída semelhante a:

Lista original: ['maçã', 'banana', 'laranja', 'maçã', 'pera', 'banana']
Como um conjunto: {'laranja', 'banana', 'maçã', 'pera'}
De volta para lista: ['laranja', 'banana', 'maçã', 'pera']

Observe que o conjunto removeu todas as duplicatas, mas a ordem é diferente da lista original. Quando convertemos o conjunto de volta para uma lista, a ordem ainda não é a mesma da nossa lista original.

O Problema com a Ordem

Esta conversão simples demonstra o problema que estamos tentando resolver: quando convertemos uma lista em um conjunto, perdemos a ordem original dos elementos. Se a ordem original for importante, essa abordagem não é adequada.

Vamos modificar nosso exemplo para mostrar por que isso pode ser um problema. Crie um arquivo chamado order_matters.py:

## Exemplo mostrando por que a ordem importa
etapas = ["Pré-aqueça o forno", "Misture os ingredientes", "Despeje a massa", "Asse", "Misture os ingredientes"]

## Remova duplicatas usando set
etapas_unicas = list(set(etapas))

print("Etapas originais de cozimento:", etapas)
print("Etapas únicas (usando set):", etapas_unicas)
print("A ordem é preservada?", etapas_unicas == ["Pré-aqueça o forno", "Misture os ingredientes", "Despeje a massa", "Asse"])

Execute o arquivo:

python3 order_matters.py

A saída será:

Etapas originais de cozimento: ['Pré-aqueça o forno', 'Misture os ingredientes', 'Despeje a massa', 'Asse', 'Misture os ingredientes']
Etapas únicas (usando set): ['Pré-aqueça o forno', 'Asse', 'Misture os ingredientes', 'Despeje a massa']
A ordem é preservada? False

Neste exemplo, a ordem das etapas de cozimento é crítica. Se você assar antes de misturar os ingredientes, o resultado será desastroso. Isso ilustra por que precisamos de uma maneira de preservar a ordem original ao remover duplicatas.

Preservando a Ordem ao Converter uma Lista em um Conjunto (Set)

Agora que entendemos o problema, vamos explorar métodos para converter uma lista em um conjunto, preservando a ordem original dos elementos.

Método 1: Usando um Dicionário para Preservar a Ordem

Uma abordagem é usar um dicionário para acompanhar a ordem dos elementos. Desde o Python 3.7, os dicionários mantêm a ordem de inserção por padrão.

Crie um novo arquivo chamado dict_approach.py:

## Usando um dicionário para preservar a ordem
frutas = ["maçã", "banana", "laranja", "maçã", "pera", "banana"]

## Crie um dicionário com elementos da lista como chaves
## Isso remove automaticamente duplicatas, preservando a ordem
frutas_unicas_dict = dict.fromkeys(frutas)

## Converta as chaves do dicionário de volta para uma lista
frutas_unicas = list(frutas_unicas_dict)

print("Lista original:", frutas)
print("Elementos únicos (ordem preservada):", frutas_unicas)

Execute o arquivo:

python3 dict_approach.py

Você deve ver:

Lista original: ['maçã', 'banana', 'laranja', 'maçã', 'pera', 'banana']
Elementos únicos (ordem preservada): ['maçã', 'banana', 'laranja', 'pera']

Observe que a ordem da primeira ocorrência de cada elemento é preservada.

Método 2: Usando OrderedDict

Para usuários de versões do Python anteriores a 3.7, ou para tornar a intenção mais explícita, podemos usar OrderedDict do módulo collections.

Crie um novo arquivo chamado ordered_dict_approach.py:

## Usando OrderedDict para preservar a ordem
from collections import OrderedDict

frutas = ["maçã", "banana", "laranja", "maçã", "pera", "banana"]

## Crie um OrderedDict com elementos da lista como chaves
## Isso remove automaticamente duplicatas, preservando a ordem
frutas_unicas_ordenadas = list(OrderedDict.fromkeys(frutas))

print("Lista original:", frutas)
print("Elementos únicos (ordem preservada):", frutas_unicas_ordenadas)

Execute o arquivo:

python3 ordered_dict_approach.py

A saída deve ser:

Lista original: ['maçã', 'banana', 'laranja', 'maçã', 'pera', 'banana']
Elementos únicos (ordem preservada): ['maçã', 'banana', 'laranja', 'pera']

Método 3: Usando um Loop e um Conjunto (Set) para Verificação

Outra abordagem é usar um loop e um conjunto para verificar se já vimos um elemento antes.

Crie um novo arquivo chamado loop_approach.py:

## Usando um loop e um conjunto para preservar a ordem
frutas = ["maçã", "banana", "laranja", "maçã", "pera", "banana"]

frutas_unicas = []
visto = set()

for fruta in frutas:
    if fruta not in visto:
        visto.add(fruta)
        frutas_unicas.append(fruta)

print("Lista original:", frutas)
print("Elementos únicos (ordem preservada):", frutas_unicas)

Execute o arquivo:

python3 loop_approach.py

A saída deve ser:

Lista original: ['maçã', 'banana', 'laranja', 'maçã', 'pera', 'banana']
Elementos únicos (ordem preservada): ['maçã', 'banana', 'laranja', 'pera']

Todos os três métodos alcançam o mesmo resultado: remover duplicatas, preservando a ordem da primeira ocorrência de cada elemento.

Exemplo Prático: Analisando Dados de Texto

Vamos aplicar o que aprendemos a um exemplo do mundo real: analisar a frequência de palavras em um texto, preservando a ordem da primeira aparição.

Criando uma Ferramenta de Análise de Texto

Crie um novo arquivo chamado text_analyzer.py:

def analyze_text(text):
    """
    Analisa o texto para encontrar palavras únicas na ordem da primeira aparição
    e suas frequências.
    """
    ## Divide o texto em palavras e converte para minúsculas
    palavras = text.lower().split()

    ## Remove pontuação das palavras
    palavras_limpas = [palavra.strip('.,!?:;()[]{}""\'') for palavra in palavras]

    ## Conta a frequência, preservando a ordem
    contagem_palavras = {}
    palavras_unicas_em_ordem = []

    for palavra in palavras_limpas:
        if palavra and palavra not in contagem_palavras:
            palavras_unicas_em_ordem.append(palavra)
        contagem_palavras[palavra] = contagem_palavras.get(palavra, 0) + 1

    return palavras_unicas_em_ordem, contagem_palavras

## Texto de exemplo
texto_exemplo = """
Python é incrível. Python também é fácil de aprender.
Com Python, você pode criar aplicações web, ferramentas de análise de dados,
modelos de aprendizado de máquina e muito mais. Python tem muitas bibliotecas
que tornam o desenvolvimento mais rápido. Python é versátil!
"""

## Analisa o texto
palavras_unicas, frequencias_palavras = analyze_text(texto_exemplo)

## Imprime os resultados
print("Exemplo de texto:")
print(texto_exemplo)
print("\nPalavras únicas na ordem da primeira aparição:")
print(palavras_unicas)
print("\nFrequências das palavras:")
for palavra in palavras_unicas:
    if palavra:  ## Ignora strings vazias
        print(f"'{palavra}': {frequencias_palavras[palavra]} vezes")

Execute o arquivo:

python3 text_analyzer.py

A saída mostrará as palavras únicas na ordem em que apareceram pela primeira vez no texto, juntamente com suas frequências:

Exemplo de texto:

Python é incrível. Python também é fácil de aprender.
Com Python, você pode criar aplicações web, ferramentas de análise de dados,
modelos de aprendizado de máquina e muito mais. Python tem muitas bibliotecas
que tornam o desenvolvimento mais rápido. Python é versátil!

Palavras únicas na ordem da primeira aparição:
['python', 'é', 'incrível', 'também', 'fácil', 'de', 'aprender', 'com', 'você', 'pode', 'criar', 'aplicações', 'web', 'ferramentas', 'análise', 'dados', 'modelos', 'aprendizado', 'máquina', 'e', 'muito', 'mais', 'tem', 'muitas', 'bibliotecas', 'que', 'tornam', 'o', 'desenvolvimento', 'mais', 'rápido', 'versátil']

Frequências das palavras:
'python': 5 vezes
'é': 3 vezes
'incrível': 1 vezes
'também': 1 vezes
...

Melhorando a Ferramenta

Vamos aprimorar nosso analisador de texto para lidar com cenários mais complexos. Crie um arquivo chamado improved_analyzer.py:

from collections import OrderedDict

def analyze_text_improved(text):
    """
    Uma versão aprimorada do analisador de texto que lida com cenários mais complexos
    e fornece mais estatísticas.
    """
    ## Divide o texto em palavras e converte para minúsculas
    palavras = text.lower().split()

    ## Remove pontuação das palavras
    palavras_limpas = [palavra.strip('.,!?:;()[]{}""\'') for palavra in palavras]

    ## Usa OrderedDict para preservar a ordem e contar a frequência
    contagem_palavras = OrderedDict()

    for palavra in palavras_limpas:
        if palavra:  ## Ignora strings vazias
            contagem_palavras[palavra] = contagem_palavras.get(palavra, 0) + 1

    ## Obtém estatísticas
    total_palavras = sum(contagem_palavras.values())
    contagem_palavras_unicas = len(contagem_palavras)

    return list(contagem_palavras.keys()), contagem_palavras, total_palavras, contagem_palavras_unicas

## Texto de exemplo
texto_exemplo = """
Python é incrível. Python também é fácil de aprender.
Com Python, você pode criar aplicações web, ferramentas de análise de dados,
modelos de aprendizado de máquina e muito mais. Python tem muitas bibliotecas
que tornam o desenvolvimento mais rápido. Python é versátil!
"""

## Analisa o texto
palavras_unicas, frequencias_palavras, contagem_total, contagem_unica = analyze_text_improved(texto_exemplo)

## Imprime os resultados
print("Exemplo de texto:")
print(texto_exemplo)
print("\nEstatísticas:")
print(f"Total de palavras: {contagem_total}")
print(f"Palavras únicas: {contagem_unica}")
print(f"Taxa de unicidade: {contagem_unica/contagem_total:.2%}")

print("\nTop 5 palavras mais frequentes:")
palavras_ordenadas = sorted(frequencias_palavras.items(), key=lambda x: x[1], reverse=True)
for palavra, contagem in palavras_ordenadas[:5]:
    print(f"'{palavra}': {contagem} vezes")

Execute o arquivo:

python3 improved_analyzer.py

Você deve ver a saída com estatísticas adicionais:

Exemplo de texto:

Python é incrível. Python também é fácil de aprender.
Com Python, você pode criar aplicações web, ferramentas de análise de dados,
modelos de aprendizado de máquina e muito mais. Python tem muitas bibliotecas
que tornam o desenvolvimento mais rápido. Python é versátil!

Estatísticas:
Total de palavras: 38
Palavras únicas: 30
Taxa de unicidade: 78.95%

Top 5 palavras mais frequentes:
'python': 5 vezes
'é': 3 vezes
'de': 1 vezes
'aprender': 1 vezes
'com': 1 vezes

Este exemplo prático demonstra como preservar a ordem dos elementos ao remover duplicatas pode ser útil em aplicações do mundo real, como análise de texto.

Comparação de Desempenho e Melhores Práticas

Agora que exploramos vários métodos para converter uma lista em um conjunto, preservando a ordem, vamos comparar seu desempenho e estabelecer algumas melhores práticas.

Criando um Teste de Desempenho

Crie um novo arquivo chamado performance_test.py:

import time
from collections import OrderedDict

def method1_dict(data):
    """Usando dict.fromkeys()"""
    return list(dict.fromkeys(data))

def method2_ordereddict(data):
    """Usando OrderedDict.fromkeys()"""
    return list(OrderedDict.fromkeys(data))

def method3_loop(data):
    """Usando um loop e um set"""
    result = []
    seen = set()
    for item in data:
        if item not in seen:
            seen.add(item)
            result.append(item)
    return result

def time_function(func, data, runs=100):
    """Medir o tempo de execução de uma função"""
    start_time = time.time()
    for _ in range(runs):
        func(data)
    end_time = time.time()
    return (end_time - start_time) / runs

## Dados de teste
small_list = list(range(100)) + list(range(50))  ## 150 itens, 50 duplicatas
medium_list = list(range(1000)) + list(range(500))  ## 1500 itens, 500 duplicatas
large_list = list(range(10000)) + list(range(5000))  ## 15000 itens, 5000 duplicatas

## Resultados do teste
print("Comparação de desempenho (tempo médio em segundos em 100 execuções):\n")

print("Lista pequena (150 itens, 50 duplicatas):")
print(f"dict.fromkeys():       {time_function(method1_dict, small_list):.8f}")
print(f"OrderedDict.fromkeys(): {time_function(method2_ordereddict, small_list):.8f}")
print(f"Loop e set:          {time_function(method3_loop, small_list):.8f}")

print("\nLista média (1.500 itens, 500 duplicatas):")
print(f"dict.fromkeys():       {time_function(method1_dict, medium_list):.8f}")
print(f"OrderedDict.fromkeys(): {time_function(method2_ordereddict, medium_list):.8f}")
print(f"Loop e set:          {time_function(method3_loop, medium_list):.8f}")

print("\nLista grande (15.000 itens, 5.000 duplicatas):")
print(f"dict.fromkeys():       {time_function(method1_dict, large_list):.8f}")
print(f"OrderedDict.fromkeys(): {time_function(method2_ordereddict, large_list):.8f}")
print(f"Loop e set:          {time_function(method3_loop, large_list):.8f}")

Execute o teste de desempenho:

python3 performance_test.py

A saída mostrará o desempenho de cada método com diferentes tamanhos de lista:

Comparação de desempenho (tempo médio em segundos em 100 execuções):

Lista pequena (150 itens, 50 duplicatas):
dict.fromkeys():       0.00000334
OrderedDict.fromkeys(): 0.00000453
Loop e set:          0.00000721

Lista média (1.500 itens, 500 duplicatas):
dict.fromkeys():       0.00003142
OrderedDict.fromkeys(): 0.00004123
Loop e set:          0.00007621

Lista grande (15.000 itens, 5.000 duplicatas):
dict.fromkeys():       0.00035210
OrderedDict.fromkeys(): 0.00044567
Loop e set:          0.00081245

Os números reais podem variar dependendo do seu sistema, mas você deve notar alguns padrões.

Melhores Práticas

Com base em nossos experimentos, vamos estabelecer algumas melhores práticas. Crie um arquivo chamado best_practices.py:

"""
Melhores Práticas para Converter uma Lista em um Conjunto Preservando a Ordem
"""

## Exemplo 1: Para Python 3.7+, use dict.fromkeys() para melhor desempenho
def preserve_order_modern(lst):
    """Melhor método para Python 3.7+ - usando dict.fromkeys()"""
    return list(dict.fromkeys(lst))

## Exemplo 2: Para compatibilidade com versões mais antigas do Python, use OrderedDict
from collections import OrderedDict

def preserve_order_compatible(lst):
    """Método compatível para todas as versões do Python - usando OrderedDict"""
    return list(OrderedDict.fromkeys(lst))

## Exemplo 3: Quando você precisa processar elementos enquanto preserva a ordem
def preserve_order_with_processing(lst):
    """Processa elementos enquanto preserva a ordem"""
    result = []
    seen = set()

    for item in lst:
        ## Opção para processar o item aqui
        processed_item = str(item).lower()  ## Exemplo de processamento

        if processed_item not in seen:
            seen.add(processed_item)
            result.append(item)  ## Mantém o item original no resultado

    return result

## Demonstração
data = ["Apple", "banana", "Orange", "apple", "Pear", "BANANA"]

print("Lista original:", data)
print("Método 1 (Python 3.7+):", preserve_order_modern(data))
print("Método 2 (Compatível):", preserve_order_compatible(data))
print("Método 3 (Com processamento):", preserve_order_with_processing(data))

Execute o arquivo:

python3 best_practices.py

A saída mostra como cada método lida com os dados:

Lista original: ['Apple', 'banana', 'Orange', 'apple', 'Pear', 'BANANA']
Método 1 (Python 3.7+): ['Apple', 'banana', 'Orange', 'apple', 'Pear', 'BANANA']
Método 2 (Compatível): ['Apple', 'banana', 'Orange', 'apple', 'Pear', 'BANANA']
Método 3 (Com processamento): ['Apple', 'Orange', 'Pear']

Observe que o Método 3 considera "Apple" e "apple" como o mesmo item devido ao processamento em minúsculas.

Recomendações

Com base em nossos experimentos, aqui estão algumas recomendações:

  1. Para Python 3.7 e posterior, use dict.fromkeys() para obter o melhor desempenho.
  2. Para compatibilidade com todas as versões do Python, use OrderedDict.fromkeys().
  3. Quando você precisar realizar um processamento personalizado ao verificar duplicatas, use a abordagem de loop e set.
  4. Considere a sensibilidade a maiúsculas e minúsculas e outras transformações com base em seus requisitos específicos.

Resumo

Neste tutorial, você aprendeu:

  1. As diferenças fundamentais entre listas e conjuntos (sets) em Python

  2. Por que converter uma lista em um conjunto normalmente faz com que a ordem seja perdida

  3. Múltiplos métodos para converter uma lista em um conjunto, preservando a ordem original:

    • Usando dict.fromkeys() no Python 3.7+
    • Usando OrderedDict.fromkeys() para compatibilidade com versões mais antigas do Python
    • Usando um loop com um conjunto (set) para processamento mais complexo
  4. Como aplicar essas técnicas a problemas do mundo real, como análise de texto

  5. Considerações de desempenho e melhores práticas para diferentes cenários

Essas técnicas são valiosas para limpeza de dados, remoção de duplicatas de entrada do usuário, processamento de opções de configuração e muitas outras tarefas comuns de programação. Ao escolher a abordagem certa com base em seus requisitos específicos, você pode escrever um código Python mais limpo e eficiente.