0

I have this scenario

class A
{
    public string id { get; set; }
    public string val1 { get; set; }
    public string val2 { get; set; }
    public string val3 { get; set; }
    public string val4 { get; set; }
    public string val5 { get; set; }
}

class B
{
    public string id { get; set; }
    public string val2 { get; set; }
    public string val3 { get; set; }
}

List<A> lista = new List<A>(); //10 items

List<B> listb = new List<B>(); //400 items

I want to store in A, the values of B that match the id field (val2 and val3 of B into val2 and val3 of A, by id) , how can I do this?

2
  • Create new A() and copy properties from B, and add this new object instead of B. Commented Apr 20, 2018 at 22:25
  • Your class design is pretty odd. Asuming those fields are actually of the same type in practice, you may want to use a nested array. Or maybe a Tupel instead of a custom class. If it is not that ideal, Linq is pretty much the prefered way, Commented Apr 20, 2018 at 23:39

3 Answers 3

1

The following LINQ query should get you there:

lista.AddRange(from b in listb
               let a = new A { id = b.id, val2 = b.val2, val3 = b.val3}
               where lista.Any(x => x.id == a.id)
               select a);

This will select a list of all B that share an ID with a value in lista, then add the result to lista.

Sign up to request clarification or add additional context in comments.

4 Comments

lista.FirstOrDefault(x => x.id == a.id) != null can be simplified to lista.Any(x => x.id == a.id)
@Aominè Good catch.
I think this is what the OP wants after reading the description several times. +1
Also, it is more efficient to do from b in listb where lista.Any(x => x.id == b.id) select new A { id = b.id, val2 = b.val2, val3 = b.val3 }; as this means you only create the A objects when you need to .
1

Creating new objects

You can use LINQ/Select to convert the B's into A's and add them with AddRange.

lista.AddRange
(
    listb.Select
    (
        b => new A 
        { 
            id   = b.id, 
            val2 = b.val2, 
            val3 = b.val3 
        } 
    )
);

Putting both types of objects in the same list

Or, if you literally want to store both A's and B's in the same list, you can set up an inheritance relationship:

class B
{
    public string id { get; set; }
    public string val2 { get; set; }
    public string val3 { get; set; }
}

class A : B
{
    public string val1 { get; set; }
    public string val4 { get; set; }
    public string val5 { get; set; }
}

var listb = new List<B>();

Now your list can store both A and B, because A is a specialization of B. So you can do this:

listb.AddRange(lista);

Updating in place

If you want to keep the original objects in lista and just want to update the matching properties, you should probably use a loop. It's possible to do it with LINQ if you really want, but it's really not "meant" for updating things in place.

It does make things a little easier to store your As in a dictionary, since you get the lookup ability. A dictionary uses a hash table internally so you will also get better performance than a scan. Because the dictionary only contains references, the objects in the original list will get updated too.

var dictionarya = lista.ToDictionary
(
    item => item.id,
    item => item
);
foreach (var b in listb)
{
    A a;
    if (dictionarya.TryGetValue(b.id, out a))
    {
        a.val2 = b.val2;
        a.val3 = b.val3;
    }
}

2 Comments

but this doesn't check if the ids match.
Revised with an additional answer for id-matching update.
0

Seems like a good job for the Join clause:

var result = lista.Join(listb,
                       a => a.id,
                       b => b.id,
                       (a, b) => 
                       new A { id = a.id, val2 = b.val2, val3 = b.val3 });

This will accomplish your description of "I want to store in A, the values of B that match the id field".

if instead, you want to accumulate the result from the Join clause to lista, then you can utilise the AddRange method:

lista.AddRange(result);

Comments

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.