0

I have two tables with the following layout

 username | displayname            username| FirstName | surname
 ---------|------------            -----------------------------
 foo        test                    foo    | andrew    | blah
 fa         display    

What I'm trying to do is display all of the displayname entries in the left hand table on my page. However, if there is a match on the username in the right hand table, I want the value test to be replaced by andrew.

Example out:

 username | displayname    
 ---------|------------      
 foo        andrew                    
 fa         display 

Everything is a string.

Below is my attempt at what I need but I've messed it up as its applying the firstname value to every entry in my first table.

_users = UserService.GetAll().ToList();
_updates = UserUpdateService.GetAll().ToList();

foreach (var user in _users)
{
    foreach (var update in _updates)
        if (user.FirstName != "" && user.UserName == update.UserName)
        {
            update.DisplayName = user.FirstName;
        }
        else
        {
            update.DisplayName = user.UserName;
        }
}

Is there a way to do this with a single LINQ query?

N0xus
  • 2,674
  • 12
  • 65
  • 126
  • 1
    as you literally mentioned the Q in LINQ stands for **query**, not **modify**. Thus what you want to achieve is not possible - or only with dirty hacks. – MakePeaceGreatAgain Jan 22 '21 at 12:34
  • Your code looks pretty fine to me. LINQing it won´t give you any benefit, in fact it will most likely make your code uglier. – MakePeaceGreatAgain Jan 22 '21 at 12:38
  • @HimBromBeere the issue is if I have a second entry in the other table and 'john' was the firstname value, then 'john' is applied to every display name value in the first table – N0xus Jan 22 '21 at 12:42
  • 1
    That has nothing to do with if or if not to use LINQ, but with your business-logic. LINQ is just syntactic sugar, it adds absoluetely no **behaviour** to your code. So if you can´t do ith with a classic loop, you can´t do it with LINQ neither. – MakePeaceGreatAgain Jan 22 '21 at 12:46
  • @HimBromBeere can you show me the correct logic for achieving what I need? – N0xus Jan 22 '21 at 12:49
  • seems like you want to **join** on `username`. – MakePeaceGreatAgain Jan 22 '21 at 12:50
  • I'd turn the second table into a dictionary to do the lookup on with `ToDictionary(x => x.username, x => x.FirstName)` then you can user `TryGetValue` to see if there is a matching username. – juharr Jan 22 '21 at 13:04

3 Answers3

1

As mentioned in the comment by @HimBromBeere which I also agree that --- LINQ is just syntactic sugar, it adds absoluetely no behaviour to your code.

As to write different style of writing you can use the below code which uses few Linq operators

_users = UserService.GetAll().ToList();
_updates = UserUpdateService.GetAll().ToList();
_updates.ForEach(u => 
{
    var record == _users.FirstOrDefault(us => !string.IsNullOrEmpty(us.FirstName) && string.Equals(us.UserName, u.UserName));
    u.DisplayName = record != null ? record.FirstName : u.DisplayName;
});
user1672994
  • 10,509
  • 1
  • 19
  • 32
0

It won't be exactly as you posted, because we are going to be creating new instances instead of modifying existing ones, but you can do this query:

var table = _users
  .Select(user => new {
    user.UserName,
    DisplayName = _updates.Any(update => 
       user.FirstName != "" && user.UserName == update.UserName) 
    ?  user.FirstName 
    :  user.UserName 
  })
);
Pablo Montilla
  • 2,941
  • 1
  • 31
  • 35
0

This is ordinal LINQ query with LEFT JOIN, don't try to do that with lists.

var users = UserService.GetAll();
var updates = UserUpdateService.GetAll();

var query = 
  from update in updates
  join user in users on user.UserName equals update.UserName into gj
  from user in gj.DefaultIfEmpty()
  select new 
  {
     update.UserName,
     DisplayName = !string.IsNullOrEmpty(user.FirstName) ? user.FirstName : update.DisplayName
  };

var result = query.ToList();
Svyatoslav Danyliv
  • 21,911
  • 3
  • 16
  • 32