1
\$\begingroup\$

Code

List<Element> elements = GetElementsList();

ISet<string> usedInnerElements = new HashSet<string>();
foreach (Element element in elements)
{
    foreach (InnerElement innerElement in element.InnerCollection.InnerElements)
    {
        usedInnerElements.Add(innerElement.SomeValue);
    }
}

class Element 
{
    public InnerCollection InnerCollection { get; set; }
}

class InnerCollection
{
    public List<InnerElement> InnerElements { get; set; }
}

class InnerElement
{
    public string SomeValue { get; set; }
}

This code scan inner collections of some other collection and save only unique values.

Question

Is there any way to present it in more fluent way using LINQ method syntax?

\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

If we can assume that none of the values can be null then the simplest solution I can think of:

ISet<string> usedInnerElements = elements
    .SelectMany(coll => coll.InnerCollection.InnerElements
        .Select(elem => elem.SomeValue))
    .Distinct()
    .ToHashSet();
  1. With SelectMany we are reducing the dimensions (flattening) from IEnumerable<IEnumerable<string>> to IEnumerable<string>
  2. With Select we retrieve the string value from its wrapper class
  3. With Distinct we are making sure that we are getting rid of from all duplicates
  4. With ToHashSet we simply convert the IEnumerable<string> to ISet<string>

Test Input:

var elements = new List<Element>
{
    new Element
    {
        InnerCollection = new InnerCollection
        {
            InnerElements = new List<InnerElement>
            {
                new InnerElement { SomeValue = "A" },
                new InnerElement { SomeValue = "C" },
                new InnerElement { SomeValue = "E" },
            }
        }
    },
    new Element
    {
        InnerCollection = new InnerCollection
        {
            InnerElements = new List<InnerElement>
            {
                new InnerElement { SomeValue = "E" },
                new InnerElement { SomeValue = "A" },
                new InnerElement { SomeValue = "B" },
            }
        }
    }
};
...
Console.WriteLine(string.Join(",", usedInnerElements));

Test output:

A,C,E,B

If you have to deal with null values then the query might look like this:

ISet<string> usedInnerElements = elements
    .Where(coll => coll != null
        && coll.InnerCollection != null
        && coll.InnerCollection.InnerElements != null)
    .SelectMany(coll => coll.InnerCollection.InnerElements
        .Where(elem => elem != null)
        .Select(elem => elem.SomeValue))
    .Distinct()
    .ToHashSet();

Test input

var elements = new List<Element>
{
    null,
    new Element
    {
        InnerCollection = null
    },
    new Element
    {
        InnerCollection = new InnerCollection
        {
            InnerElements = null
        }
    },
    new Element
    {
        InnerCollection = new InnerCollection
        {
            InnerElements = new List<InnerElement>
            {
                new InnerElement { SomeValue = "E" },
                new InnerElement(),
                null
            }
        }
    }
};

Test output

E,
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.