[ANSWER] Here are Common Mistakes When Working with Enumerables. Can you see it?

Write code is not a simple task. It is easy to make mistakes that result in bad performance. The last post, I asked what is wrong with the following code:

public static IEnumerable<T> ElementsFromTheFirstWhichAreAlsoInTheSecond<T>(
    IEnumerable<T> enumerable1,
    IEnumerable<T> enumerable2
)
{
    var result = new List<T>();

    enumerable1.ToList().ForEach(a =>
    {
        if (enumerable2.Contains(a))
        {
            result.Add(a);
        }
    });

    return result;
}

Let’s try to explain what is wrong.

Mistake #1 – ToList!

From last post comments:

For some reason, a few developers like so much to write inline code. So, this pattern enumerable1.ToList().ForEach is very common. Problem is ToList will iterate all elements of enumerable to create a new object (more complex) List. – Tiago Borba

I think the “developers reason” is to try to adopt a “functional style.” Unfortunately in a wrong way.

Let me explain what Tiago was talking about with an example. When running the following code:

class Program
{
    static void Main(string[] args)
    {
        var enumerable = TenInstances().ToList();

        foreach (var foo in enumerable.Take(3))
        {
            Console.WriteLine(foo.Value);
        }
    }

    public static IEnumerable<Foo> TenInstances()
    {
        for (var i = 0; i < 10; i++)
        {
            yield return new Foo(i);
        }
    }    
}

class Foo
{
    public int Value { get; }

    public Foo(int value)
    {
        Value = value;
        Console.WriteLine($"Created {value}.");
    }
}

The output is:

As you can see, we are creating ten objects when we need only 3. Stop calling ToList() would change the result dramatically:

Did you get the point? We should never call ToList without a good reason.

It’s time to return to the code which motivated this post. Let’s dig a little deeper.

 
.method public hidebysig static 
	class [System.Runtime]IEnumerable`1<!!T> ElementsFromTheFirstWhichAreAlsoInTheSecond<T> (
		class [System.Runtime]IEnumerable`1<!!T> enumerable1,
		class [System.Runtime]IEnumerable`1<!!T> enumerable2
	) cil managed 
{
	.maxstack 3
	.locals init (
		[0] class Program/'<>c__DisplayClass1_0`1'<!!T>
	)

	IL_0000: newobj instance void class Program/'<>c__DisplayClass1_0`1'<!!T>::.ctor()
	IL_0005: stloc.0
	IL_0006: ldloc.0
	IL_0007: ldarg.1
	IL_0008: stfld class [System.Runtime]IEnumerable`1<!0> class Program/'<>c__DisplayClass1_0`1'<!!T>::enumerable2
	IL_000d: ldloc.0
	IL_000e: newobj instance void class [System.Collections]List`1<!!T>::.ctor()
	IL_0013: stfld class [System.Collections]List`1<!0> class Program/'<>c__DisplayClass1_0`1'<!!T>::result
	IL_0018: ldarg.0
	IL_0019: call class [System.Collections]List`1<!!0> [System.Linq]Enumerable::ToList<!!T>(class [System.Runtime]IEnumerable`1<!!0>)
	IL_001e: ldloc.0
	IL_001f: ldftn instance void class Program/'<>c__DisplayClass1_0`1'<!!T>::'<ElementsFromTheFirstWhichAreAlsoInTheSecond>b__0'(!0)
	IL_0025: newobj instance void class [System.Runtime]Action`1<!!T>::.ctor(object, native int)
	IL_002a: callvirt instance void class [System.Collections]List`1<!!T>::ForEach(class [System.Runtime]System.Action`1<!0>)
	IL_002f: ldloc.0
	IL_0030: ldfld class [System.Collections]List`1<!0> class ConsoleApp1.Program/'<>c__DisplayClass1_0`1'<!!T>::result
	IL_0035: ret
}

As you can see, there are two unnecessary allocations here: 1) for the List object and; 2) for the closure. The problem is it is not really necessary.

My recommendation? Goodbye, ToList().

public static IEnumerable<T> ElementsFromTheFirstWhichAreAlsoInTheSecond<T>(
    IEnumerable<T> enumerable1,
    IEnumerable<T> enumerable2)
{
    var result = new List<T>();
    foreach (var a in enumerable1)
    {
        if (enumerable2.Contains(a))
        {
            result.Add(a);
        }
    }
    return result;
}

But there are other considerations.

Mistake #2 – Unnecessary Iterations Caused by Contains Method

Please consider the following code:

static void Main(string[] args)
{
    var result = ElementsFromTheFirstWhichAreAlsoInTheSecond(
        Enumerable1(),
        Enumerable2()
    );
}

public static IEnumerable<int> Enumerable1()
{
    for (int i = 5; i >= 0; i--)
    {
        Console.WriteLine($"Enumerable 1: {i}");
        yield return i;
    }
}

public static IEnumerable<int> Enumerable2()
{
    for (int i = 0; i < 5; i++)
    {
        Console.WriteLine($"Enumerable 2: {i}");
        yield return i;
    }
}

public static IEnumerable<T> ElementsFromTheFirstWhichAreAlsoInTheSecond<T>(
    IEnumerable<T> enumerable1,
    IEnumerable<T> enumerable2)
{
    var result = new List<T>();
    foreach (var a in enumerable1)
    {
        if (enumerable2.Contains(a))
        {
            result.Add(a);
        }
    }
    return result;
}

Here is the output:

 

As you can see, the Contains will restart the enumeration in every call.

My recommendation? Ensure that the enumeration will run only one time.

public static IEnumerable ElementsFromTheFirstWhichAreAlsoInTheSecond(
    IEnumerable enumerable1,
    IEnumerable enumerable2)
{
    var result = new List();
    var enumerable2AsArray = enumerable2 as T[] ?? enumerable2.ToArray();
    foreach (var a in enumerable1)
    {
        if (enumerable2AsArray.Contains(a))
        {
            result.Add(a);
        }
    }
    return result;
}

Got it?

Mistake #3 – Returning a list

If the method’s returning type is IEnumerable, use yield return instead of creating a List. That will prevent unnecessary iterations.

public static IEnumerable<T> ElementsFromTheFirstWhichAreAlsoInTheSecond<T>(
    IEnumerable<T> enumerable1,
    IEnumerable<T> enumerable2)
{
    var enumerable2AsArray = enumerable2 as T[] ?? enumerable2.ToArray();
    foreach (var a in enumerable1)
    {
        if (enumerable2AsArray.Contains(a))
        {
            yield return a;
        }
    }
}

That’s it.

If you have additional considerations, comment it.

Compartilhe este insight:

Elemar Júnior

Sou fundador e CEO da EximiaCo e atuo como tech trusted advisor ajudando diversas empresas a gerar mais resultados através da tecnologia.

Elemar Júnior

Sou fundador e CEO da EximiaCo e atuo como tech trusted advisor ajudando diversas empresas a gerar mais resultados através da tecnologia.

Mais insights para o seu negócio

Veja mais alguns estudos e reflexões que podem gerar alguns insights para o seu negócio:

I have no idea of how many times I had to write a function do discover the minimum and the...
Inspecting the code repository of a client, I found something like this: var customer = new { Id = default(int),...
Quando pensamos sobre o código-fonte do Roslyn, deveríamos pensar em performance! Eu gostaria de compartilhar algumas técnicas de performance e...
Há tempos, venho pensando sobre “mérito”. Superficialmente, quanto dos meus resultados, positivos e negativos, se devem exclusivamente a mim? Descartando...
O que mais me agrada no C4 Model é a forma como detalhes vão sendo explicitados na medida em que...
The example which motivated this post comes from the excellent book Designing Distributed Systems by Brendan Burns (co-founder of the...

Curso Reputação e Marketing Pessoal

Masterclasses

01

Introdução do curso

02

Por que sua “reputação” é importante?

03

Como você se apresenta?

04

Como você apresenta suas ideias?

05

Como usar Storytelling?

06

Você tem uma dor? Eu tenho o alívio!

07

Escrita efetiva para não escritores

08

Como aumentar (e manter) sua audiência?

09

Gatilhos! Gatilhos!

10

Triple Threat: Domine Produto, Embalagem e Distribuição

11

Estratégias Vencedoras: Desbloqueie o Poder da Teoria dos Jogos

12

Análise SWOT de sua marca pessoal

13

Soterrado por informações? Aprenda a fazer gestão do conhecimento pessoal, do jeito certo

14

Vendo além do óbvio com a Pentad de Burkle

15

Construindo Reputação através de Métricas: A Arte de Alinhar Expectativas com Lag e Lead Measures

16

A Tríade da Liderança: Navegando entre Líder, Liderado e Contexto no Mundo do Marketing Pessoal

17

Análise PESTEL para Marketing Pessoal

18

Canvas de Proposta de Valor para Marca Pessoal

19

Método OKR para Objetivos Pessoais

20

Análise de Competências de Gallup

21

Feedback 360 Graus para Autoavaliação

22

Modelo de Cinco Forças de Porter

23

Estratégia Blue Ocean para Diferenciação Pessoal

24

Análise de Tendências para Previsão de Mercado

25

Design Thinking para Inovação Pessoal

26

Metodologia Agile para Desenvolvimento Pessoal

27

Análise de Redes Sociais para Ampliar Conexões

Lições complementares

28

Apresentando-se do Jeito Certo

29

O mercado remunera raridade? Como evidenciar a sua?

30

O que pode estar te impedindo de ter sucesso

Recomendações de Leituras

31

Aprendendo a qualificar sua reputação do jeito certo

32

Quem é você?

33

Qual a sua “IDEIA”?

34

StoryTelling

35

Você tem uma dor? Eu tenho o alívio!

36

Escrita efetiva para não escritores

37

Gatilhos!

38

Triple Threat: Domine Produto, Embalagem e Distribuição

39

Estratégias Vencedoras: Desbloqueie o Poder da Teoria do Jogos

40

Análise SWOT de sua marca pessoal

Inscrição realizada com sucesso!

No dia da masterclass você receberá um e-mail com um link para acompanhar a aula ao vivo. Até lá!

A sua subscrição foi enviada com sucesso!

Aguarde, em breve entraremos em contato com você para lhe fornecer mais informações sobre como participar da mentoria.

Masterclass
15/07

Pare de dar a solução certa para o problema errado

Muita gente boa quebra a cabeça por dias tentando resolver o que não estava quebrado, simplesmente por tentar dar a resposta certa pro problema errado, mas precisa realmente ser assim?

Crie sua conta

Preencha os dados para iniciar o seu cadastro no plano anual do Clube de Estudos:

Crie sua conta

Preencha os dados para iniciar o seu cadastro no plano mensal do Clube de Estudos:

× Precisa de ajuda?