1

Is there a way to rewrite the GetTransformedCollection method below so that it uses a Linq statement and not an expression? I'm currently trying to get around the “A lambda expression with a statement body cannot be converted to an expression tree” error.

public class Obj1
{
    public int Id { get; set; }
    public string[] Names { get; set; }
    public string[] Tags { get; set; }
}

public class EntCollections
{
    private List<Obj1> _results;

    [SetUp]
    public void SetUp()
    {
        _results = new List<Obj1>
        {
            new Obj1 {Id = 1, Names = new[] {"n1"}, Tags = new[] {"abc", "def"}},
            new Obj1 {Id = 2, Names = new[] {"n2", "n3"}, Tags = new[] {"ghi"}},
            new Obj1 {Id = 3, Names = new[] {"n1", "n3"}, Tags = new[] {"def", "xyz"}}
        };
    }

    private static Dictionary<string, List<string>>
        GetTransformedCollection(IEnumerable<Obj1> results)
    {
        var list = new Dictionary<string, List<string>>();

        foreach (var result in results)
        {
            foreach (var id in result.Names)
            {
                if (list.ContainsKey(id))
                {
                    list[id].AddRange(result.Tags);
                }
                else
                {
                    list.Add(id, result.Tags.ToList());
                }
            }
        }

        return list;
    }

    [Test]
    public void Test()
    {
        var list = GetTransformedCollection(_results);

        Assert.That(list["n1"], Is.EquivalentTo(new [] { "abc", "def", "def", "xyz" }));
        Assert.That(list["n2"], Is.EquivalentTo(new [] { "ghi" }));
        Assert.That(list["n3"], Is.EquivalentTo(new [] { "ghi", "def", "xyz" }));
    }

P.s I'm not too worried about the result type being a Dictionary, that was just the simplist way to express it as a return type.

2
  • You mean replacing foreach with Linq? Commented Jan 15, 2013 at 17:23
  • Try using Linq All() or ForEach() extensions. Commented Jan 15, 2013 at 17:26

2 Answers 2

7

I would personally use an ILookup, which is a good bet whenever you have a Dictionary<T1, List<T2>>, and is built with ToLookup():

// Flatten the objects (lazily) to create a sequence of valid name/tag pairs
var pairs = from result in results
            from name in result.Names
            from tag in result.Tags
            select new { name, tag };

// Build a lookup from name to all tags with that name
var lookup = pairs.ToLookup(pair => pair.name, pair => pair.tag);
Sign up to request clarification or add additional context in comments.

6 Comments

+1. Much cleaner solution, than using extension method syntax
I'm coming around to this approach as it only enumerates the list once. Is there a way to perform an 'outer join' on the list? By this I mean if an element exists in the original list with n Names but 0 Tags, then the individual names will still appear in the result set as a pair with null/empty string? I've appended such an element the original list and added an extra assertion.
That sounds like you want GroupJoin, basically. It would probably be a significantly different answer - I suggest you ask it as a separate question, given how different it will be.
Also removed the amendment to this question so the answer remains valid.
|
2

Idea is to find all keys for resulting dictionary and then find corresponding values from original sequence of Obj1

var distinctNames = results.SelectMany(val => val.Names).Distinct();

return distinctNames
      .ToDictionary(name => name, 
                    name => results
                            .Where(res => res.Names.Contains(name))
                            .SelectMany(res => res.Tags)
                            .ToList());

1 Comment

To be honest I found the mechanics of this easier to picture, thanks.

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.