Executando verificação de segurança...
7

Esqueça o If...Else: Use Hashtables em JavaScript

e você ainda está preso ao uso de if...else ou switch case no seu código JavaScript, talvez seja hora de repensar sua abordagem. Embora essas estruturas sejam úteis e bastante comuns, elas podem se tornar verbosas e difíceis de manter, especialmente em casos com muitas condições.

A boa notícia é que existe uma alternativa mais elegante e eficiente: as hashtables!

O que é uma Hashtable?

Na programação, uma hashtable (ou tabela de hash) é uma estrutura de dados que associa chaves a valores, permitindo buscas em tempo constante O(1). No JavaScript, utilizamos objetos literais e Maps para representar hashtables. Isso não só facilita a organização do código, mas também melhora a performance em diversas situações.

Por que usar Hashtables?

Performance Consistente: A busca por um valor é feita em tempo constante, independente do tamanho da estrutura.

Código Mais Limpo: Substituir estruturas condicionais aninhadas por mapeamentos chave-valor deixa o código mais organizado e fácil de ler.

Escalabilidade: Alterações na lógica de mapeamento são feitas de forma centralizada, sem precisar modificar vários blocos condicionais.

Como utilizar Hashtables para verificação condicional

Vamos imaginar um cenário comum: uma API de restaurante que retorna uma mensagem com base no status do pedido. A abordagem tradicional com if...else ficaria assim:

function getOrderStatusMessage(orderStatus) {
if (orderStatus === "pending") {
return "Seu pedido está em análise.";
} else if (orderStatus === "processing") {
return "Seu pedido está sendo preparado.";
} else if (orderStatus === "shipped") {
return "Seu pedido foi enviado.";
} else if (orderStatus === "delivered") {
return "Seu pedido foi entregue!";
} else if (orderStatus === "canceled") {
return "Seu pedido foi cancelado.";
} else {
return "Status desconhecido";
}
}

Embora funcione bem para poucos casos, esse código pode se tornar caótico conforme mais condições forem adicionadas. Vamos ver uma alternativa usando hashtable:

function getOrderStatusMessage(orderStatus) {
const orderStatusMessages = {
pending: "Seu pedido está em análise.",
processing: "Seu pedido está sendo preparado.",
shipped: "Seu pedido foi enviado.",
delivered: "Seu pedido foi entregue!",
canceled: "Seu pedido foi cancelado."
};
return orderStatusMessages[orderStatus] || "Status desconhecido";
}

console.log(getOrderStatusMessage("canceled")); // Output: "Seu pedido foi cancelado."

Exemplo Prático: Categorização de Produtos

Imagine uma API de e-commerce que precisa categorizar produtos com base no preço. Veja uma solução mais elegante usando hashtable:

const product = { name: "Escova de Cabelo", price: 50 };

function categorizeProduct(product) {
const categories = {
premium: { from: 101, to: Infinity },
regular: { from: 50, to: 100 },
basic: { from: 0, to: 49 }
};

const productCategory = Object.keys(categories).find((key) => {
const category = categories[key];
return product.price >= category.from && product.price <= category.to;
});

return { ...product, category: productCategory || null };
}

console.log(categorizeProduct(product)); // Output: { name: 'Escova de Cabelo', price: 50, category: 'regular' }

Conclusão

Ao optar por hashtables, você garante um código mais limpo, performático e fácil de manter. Não se trata apenas de evitar if...else ou switch case, mas de adotar uma abordagem que realmente faça sentido para o contexto da sua aplicação.

Experimente aplicar essa técnica em seus próximos projetos e perceba como o desenvolvimento se torna mais fluido e profissional. Se tiver dúvidas ou quiser compartilhar suas experiências, fique à vontade para comentar!

Carregando publicação patrocinada...
10

Sim, essa é uma solução válida e já usei diversas vezes, e pode até deixar o código mais legível (limpo).

Mas é bom alertar para o fato que ela é menos performática, e pode confundir alguns (o que eu não me importo muito, desde que não confunda quem sabe programar, que é o que importa, se não for um código tutorial).

Claro que você pode não se importar com a performance, porque afinal é JavaScript, que foi feito para rodar scripts e não aplicaçãoes pesadas, porque se estiver rodando isso provavelmente a escolha da linguagemfoi errada. E também provalmente está rodando em um browser, o que importa pouco, mas muita gente já roda JS em servidor, e nenhum problema se a performance não é importante, ele costuma ser em média 10x mais lento que outras tecnologias por várias razões (não estou pegando o melhor caso nem o pior).

Se a performance não importa, o fato da hash table ser O(1) nem importa muito (na verdade o pior caso é O(n), mas ele quase nunca ocorre, em alguns casos ele é só um pouco superior a O(1), e em muitos ele é efetivamente O(1)). E ser O(1) não é mesma coisa de ser rápido. Dois algoritmos O(1) que fazem a mesma coisa podem ter velocidades efetivas muito díspares, o Big O só demonstra a relação de velocidade em relação a quantidade de elemntos com potencial de serem processados.

Ao mesmo tempo que em alguns casos, especialmente com muitas opções, que o if pode ser mais lento, porque ele, no pior caso, será O(n). Mas novamente, cada elemento será bem mais aconômico que pegar um elmento na hash table, o problema é que ele terá que fazer em vários, então a conta não é simples.

Nem falei da questão que a hash table colocará pressão no garbage collector e isso trará mais custo de performance e se você não souber fazer um benchmark adequadamente pode nem pegar esse custo.

Reforçando que isso vale para quando a performance importa.

A questão de ser mais fácil de manter e ser elegante não é tão simples, até porque pode ser subjetivo, principalmente o segundo exemplo pode ser mais difícil, pelo menos para alguns.

Quando mudamos para um switch, a seleção por código começa ter mais vantagens e é uma pena que a sintaxe do JS não ajude mais como outras linguagens que tem uma pegada mais funcional, incluindo Java e C#, não só as (quase) completamente funcionais. E eu não sei as otimizações que cada engine de JS faz para falar sobre a performance dele, em algumas implmentações de linguagens o switch pode ser O(1), o que não quer dizer que será necessariamente mais rápido do que o if. Só de colocar o returnna mesma linha do if e não usar chaves já ajuda um pouco o código ficar mais curto.

Com um bom switch eu evitaria completamente o uso de hash tables em quase todos os casos, mas novamente, para JS isso não importa muito.

Aí temos uma lição para aprender. Se usamos JS como ele foi concebido originalmente, qualquer pode ser feita porque é algo simples e a performance não importa, mas quando usamos como a maioria usa hoje para algo mais complexo e que pode precisar de performance, JS pode não ser a melhor opção, apesar e atender a demanda. Sei que isso não vai agradar muita gente, mas estou falando olhando todos os aspectos da engenharia. Clar oque eu entendo que as decisões de qual linguagem usar passa muito por mercado e outras questões que não são de engenharia.

S2


Farei algo que muitos pedem para aprender a programar corretamente, gratuitamente (não vendo nada, é retribuição na minha aposentadoria) (links aqui no perfil também).

1

E no caso de usar o new Map() eu uso bastante para alguns casos, alguma vantagem?
Nesse caso de retornar um texto eu até usaria um enum descritivo.
Mas por exemplo com Map:

const orderStatusMap = new Map([
    ["pending", "Seu pedido está em análise"],
    ["processing", "Seu pedido está sendo preparado"],
]);

orderStatusMap.get("pending");
1

Nesse exemplo só aumentou a complexidade da leitura.
Se você tem uma estrutura que vai crescer, use o Map. Se tem uma estrutura estática, prefira o object {}.
Há muitos artigos de testes de benchmark mostrando a diferença e casos de uso.

1

Map é uma hash table, é praticamente a mesma coisa. Eu não sei se a implementação interna é um pouco diferente em JS, mas a base é a mesma, tem linguagens que a implementação é idêntica, uma é só syntax sugar de outra.

1

Prefiro:

// Definindo o enum de status
const OrderStatus = Object.freeze({
  PENDING: 'pending',
  PROCESSING: 'processing',
  SHIPPED: 'shipped',
  DELIVERED: 'delivered',
  CANCELED: 'canceled',
});

// Mapeando mensagens para os status
const statusMessages = Object.freeze({
  [OrderStatus.PENDING]: 'Seu pedido está em análise.',
  [OrderStatus.PROCESSING]: 'Seu pedido está sendo preparado.',
  [OrderStatus.SHIPPED]: 'Seu pedido foi enviado.',
  [OrderStatus.DELIVERED]: 'Seu pedido foi entregue!',
  [OrderStatus.CANCELED]: 'Seu pedido foi cancelado.',
});

// Função usando o mapa de mensagens
function getOrderStatusMessage(orderStatus) {
  return statusMessages[orderStatus] || 'Status desconhecido';
}

// Testes
console.log(getOrderStatusMessage(OrderStatus.PENDING)); // "Seu pedido está em análise."
console.log(getOrderStatusMessage(OrderStatus.SHIPPED)); // "Seu pedido foi enviado."
console.log(getOrderStatusMessage('unknown')); // "Status desconhecido"
1
1

Finalmente uma resposta adequada.
Vejo muito o pessoal adicionando complexidade no que foi concebido para ser simples e performático.
Um if com return sem precisar de else if, já melhora a legibilidade.
O switch case então... Para ler um código de cima pra baixo fica muito simples do que ter que mapear uma outra estrutura de objetos apenas por conveniência. Fora que ele foi concebido pra isso. O compilador otimizará o máximo possível para performá-lo.

2

Sim, não deixa de ser uma solução válida. Já usei e isso deixa o código ligeiramente elegante, mas vai variar de acordo com as circunstâncias e tem seus trade-offs.
É muito útil para evitar funções gigantescas com vários "ifs" e "else ifs", mas, para fluxos que apresentam decisões mais simples, isso não seria necessário e poderia ser um baita overengineering. Além disso, o uso desenfreado de dicionários para armazenar as decisões condicionais poderia levar a um consumo exagerado de memória em um sistema, quando, na verdade, você só precisaria fazer um:

if (condicao) {
    return x;
}
return y;

O título "Esqueça o if/else" se torna inadequado, pois acaba confundindo pessoas com menos experiência. Algumas pessoas poderiam acabar se tornando "o chato de uma seita anti-ifs" e complicando muito o que deveria ser mais simples, nao agregando em nada no core de uma discussao tecnica de alguem que nao entendeu nada dos problemas resolvidos, repetindo apenas "to vendo q vcs estao usando blocos condicionais demais, vamos refatorar".

2

Dependendo do tamanho da sua equipe e sua diversidade linguistica (digo aqui, cada programador tem um jeito diferente de ler/escrever código, e a maioria nao sabe nem padrões de projeto), eu sempre opto pelo mais simples, então eu faria assim:

function getOrderStatusMessage(orderStatus) {
  if (orderStatus === "pending") return "Seu pedido está em análise";
  if (orderStatus === "processing") return "Seu pedido está sendo preparado.";
  if (orderStatus === "shipped") return "Seu pedido foi enviado.";
  if (orderStatus === "delivered") return "Seu pedido foi entregue!";
  if (orderStatus === "canceled") return "Seu pedido foi cancelado.";
  return "Status desconhecido";
}

Agora, geralmente quando existe um campo "status" ou um campo "tipo", geralmente há a necessidade de verificar através de if algum tipo específico de status. Nesse caso, eu já crio um enum de objetos. É 0,000001 mais lento, mas o que importa é a facilidade de leitura do programador.

const OrderStatus = {
  PENDING: "pending",
  PROCESSING: "processing",
  SHIPPED: "shipped",
  DELIVERED: "delivered",
  CANCELED: "canceled",
};

const OrderStatusDescriptions = {
  [OrderStatus.PENDING]: "Seu pedido está em análise",
  [OrderStatus.PROCESSING]: "Seu pedido está sendo preparado.",
  [OrderStatus.SHIPPED]: "Seu pedido foi enviado.",
  [OrderStatus.DELIVERED]: "Seu pedido foi entregue!",
  [OrderStatus.CANCELED]: "Seu pedido foi cancelado.",
};

function getOrderStatusMessage(orderStatus) {
  return OrderStatusDescriptions[orderStatus] || "Status desconhecido";
}

// Exemplo de uso com IF:
const orderStatus = OrderStatus.SHIPPED;

if (orderStatus === OrderStatus.SHIPPED) {
  console.log("O pedido já foi enviado!");
} else if (orderStatus === OrderStatus.DELIVERED) {
  console.log("O pedido já foi entregue!");
} else {
  console.log(getOrderStatusMessage(orderStatus));
}

// Saída esperada: "O pedido já foi enviado!"

Nesse segundo código, a unica diferença é que o const está fora da funcão, e pode ser reutilizado na app inteira caso necessário. Inclusive eu colocaria em um arquivo OrderStatusEnum.js e usaria export.

1

Resposta ao Artigo: "Hashtables vs. If...Else: A Saga Continua (com Risadas)"

Primeiramente, parabéns ao Leandrocmore pelo artigo! A discussão sobre hashtables vs. if...else é quase tão antiga quanto debater se espaço ou tabulação é melhor para indentação (time espaço aqui! 😉).
Adorei a analogia do restaurante! Me fez pensar: e se o garçom, em vez de usar uma elegante hashtable mental para lembrar seu pedido, usasse um gigantesco fluxograma if...else?

if (cliente == "Mesa 1") {
  if (pedido == "Pizza") {
    if (sabor == "Calabresa") {
      if (borda == "Catupiry") {
        // ... umas 50 linhas depois ...
        return "Uma pizza de calabresa com borda de catupiry para a Mesa 1";
      } else {
        return "Senhor, qual era a borda mesmo?"; // Já esqueceu!
      }
    }
  }
} else if (cliente == "Mesa 2") {
  // ... mais umas 100 linhas ...
}

Aposto que até o pedido mais simples chegaria frio! 😅
Brincadeiras à parte, o ponto central é excelente: hashtables podem ser superpoderosas para organizar e agilizar nosso código. Mas, como o próprio artigo sutilmente sugere, não precisamos jogar o bom e velho if...else no lixo!
É como ter uma caixa de ferramentas: você tem o martelo (hashtable, para mapeamentos diretos e busca rápida!), a chave de fenda (if...else, para condições mais complexas e ramificações!), e até o canivete suíço (uma mistura dos dois, quando a situação realmente exige!).
O exemplo da categorização de produtos é um ótimo exemplo! A hashtable define os intervalos, mas o find dentro da função é, na verdade, um disfarçado if...else dando um "oi" ali no meio. E não tem nada de errado com isso! É a ferramenta certa para o trabalho!
A grande sacada é:

  • Conheça suas ferramentas: Entenda as forças e fraquezas de cada estrutura. Hashtables são ótimas para buscar coisas rapidamente, mas podem consumir mais memória. If...else são flexíveis, mas podem virar um monstro espaguete se não forem bem organizados.
  • Escolha a ferramenta certa: Não existe solução mágica. Às vezes, um simples if resolve. Outras vezes, uma hashtable elegante é a chave. E, em alguns casos, uma combinação dos dois é a obra-prima!
  • Não tenha medo de experimentar (e refatorar!): Começou com if...else e ficou gigante? Tente uma hashtable! A hashtable ficou confusa? Volte para o if...else! O importante é ter um código limpo, legível e que funcione (de preferência, antes do cliente do restaurante ir embora de fome!).
    Então, da próxima vez que você se deparar com uma cadeia de if...else que parece o roteiro de um filme do Christopher Nolan, lembre-se das hashtables! Mas não se esqueça: o if...else ainda é seu amigo, e às vezes, a simplicidade é a melhor solução.
    E, por favor, continuem escrevendo artigos como este! Adoro uma boa discussão (e um bom meme sobre programação). 😉
1

Não sabia que isso eram hashtables kk sempre fiz coisas do tipo(ainda sou meio iniciante e bom tempo só programei por hobbie), mas acho que o titulo não devia ser sobre javascript porque assim você ta limitando seu post, isso dai é valido para muitas linguagens de programação.

1

"Esqueça o if/else".

Não gosto desse tipo de título porque pode confundir iniciantes. Não há absolutamente nada errado em usar if/else.

Para esse caso do post, hashmaps fazem sentido. Mas para outros, if/else e switches podem ser opções melhores.

1

Clickbait fraco esse título hein?

Cada coisa tem seu uso, se voce precisa de uma decisão lógica no seu código, não tem como deixar de usar if/else. Se tem que só retornar uma string de acordo com um valor, fica muito mais organizado usar hashtable ou qualquer outra estrutura de tabela.

E se essa decisão afeta a performance do seu sistema, você não é o público alvo dessa matéria.

1
0
2
0
-1