Paul Michaels
Paul Michaels

Reputation: 16685

Lambda expression not behaving as expected

I'm trying to construct a lambda expression that will match elements of one array with a second. Below is a simplified version of this query:

class Program
{
    static void Main(string[] args)
    {
        string[] listOne = new string[] { "test1", "test2", "test3" };
        MyClass[] listTwo = new MyClass[] { new MyClass("test1") };

        string[] newVals = listOne.Where(p => listTwo.Select(e => e.Name).Equals(p)).ToArray();

        //string[] newVals2 = listOne.Intersect(listTwo.Select(t => t.Name)).ToArray();
    }

    class MyClass
    {
        public MyClass(string name)
        {
            Name = name;
        }
        public string Name {get; set;}
    }
}

I would expect newVals to return an array of 1 value, but it's empty. I realise that uncommenting myVals2 would achieve the same result, but the lists of classes differ more fundamentally than shown.

Upvotes: 1

Views: 109

Answers (5)

TYY
TYY

Reputation: 2716

You are trying to perform a join, technically you are better off simplifying your linq statement to use a join. An example is included below.

    static void Main(string[] args)
    {
        string[] listOne = new [] { "test1", "test2", "test3" };
        MyClass[] listTwo = new [] { new MyClass("test1") };

        string[] newVals = (from str1 in listOne
                           join str2 in  listTwo.Select(e => e.Name) on str1 equals str2
                           select str1).ToArray();
        foreach (var newVal in newVals)
        {
            Console.WriteLine(newVal);
        }

        //string[] newVals2 = listOne.Intersect(listTwo.Select(t => t.Name)).ToArray();
    }

    class MyClass
    {
        public MyClass(string name)
        {
            Name = name;
        }
        public string Name { get; set; }
    }

Upvotes: 0

DaveShaw
DaveShaw

Reputation: 52788

You probably want to perform a Join on the 2 collections.

var q = 
  listOne
  .Join(
    listTwo,
    l2 => l2,
    l1 => l1.Name,
    (l2, l1) => new { l2, l1, });

You can change the selector (the last parameter) to suit your needs, if it's just values from listOne for example then have (l2, l1) => l1.

The other solutions will work, but maybe not as you would expect.

Using Linq-Objects Contains within a where clause will cause the entire of listTwo to be iterated for each entry in listOne.

Upvotes: 2

dutzu
dutzu

Reputation: 3910

How about something like this:

string[] newVals = listOne.Where(p => listTwo.Any(e => e.Name.Contains(p))).ToArray();

or to be more strict use == instead of Contains.

But if you want to obtain the items that are common between the 2 why not just call .Intersect()??

Upvotes: 1

Hamlet Hakobyan
Hamlet Hakobyan

Reputation: 33381

Try this:

string[] listOne = new string[] { "test1", "test2", "test3" };
        MyClass[] listTwo = new MyClass[] { new MyClass("test1") };

        string[] newVals = listOne
                       .Where(p => listTwo.Select(e => e.Name).Contains(p))
                       .ToArray();

listTwo.Select(e => e.Name) is a IEnumerable<string>

Upvotes: 2

Zbigniew
Zbigniew

Reputation: 27584

You are using Equals but you should use Contains. You are checking wheter IEnumerable<> is equal to p, but you want to check if IEnumerable<> contains p, so replace:

string[] newVals = listOne.
                   Where(p => listTwo.Select(e => e.Name).Equals(p)).
                   ToArray();

with

string[] newVals = listOne.
                   Where(p => listTwo.Select(e => e.Name).Contains(p)).
                   ToArray();

Upvotes: 4

Related Questions