Gab
Gab

Reputation: 531

Replace single node in c# tree

I have a tree construct in my application which contains hierarchical data of users.

public class User {
     //a lot of properties here like Id, Name, Last Name, etc.
     public IEnumerable<User> Employees {get;set;}
}

Now I need to replace certain record in this tree with new item.

var changedUsers = GetChangedUsers();
// Replace all changed user fields in original hierarchy 
// (only FirstName, LastName, etc. without touching Employees field)

Is there an elegant way to achieve this?

EDIT:

I have tried to loop with recursion, but stuck with updating record.

private void ReplaceUserInHierarchy(User modifiedUser, List<User> users)
        {
            foreach (var user in users)
            {
                if (user.Id == modifiedUser.Id)
                {
                    //we should update here somehow
                    return;
                }

                ReplaceUserInHierarchy(modifiedUser, user.Employees);
            }
        }

Upvotes: 1

Views: 152

Answers (1)

Sergey Berezovskiy
Sergey Berezovskiy

Reputation: 236228

Simply update user wich matched given id:

private void ReplaceUserInHierarchy(User modifiedUser, List<User> users)
{
    foreach (var user in users)
    {
        if (user.Id == modifiedUser.Id)
        {
            // update properties of user here
            user.FirstName = modifiedUser.FirstName;
            // etc
            return; // if user can be duplicated in hierarchy, then use continue here
        }

        // assume user cannot be in own employees hierarchy
        ReplaceUserInHierarchy(modifiedUser, user.Employees);
    }
}

Improving elegance - you can use extension method which flattens hierarchy. Then search for the user you should update will look like:

var user = users.Flatten(u => u.Employees).FirstOrDefault(u => u.Id == modifiedUser.Id);
if (user != null) // you can throw if user not found
    user.FirstName = modifiedUser.FirstName; // etc        

Flattening can be done as

public static IEnumerable<T> Flatten<T>(
   this IEnumerable<T> source,
   Func<T, IEnumerable<T>> selector)
{
   // null-check arguments
   foreach(var item in source)
   {
       yield return item;
       foreach(var child in Flatten(selector(item), selector))
           yield return child;
   }   
}

Further improvements - you can save flattened hierarchy to the dictionary and use it to update several users:

var usersById = users.Flatten(u => u.Employees).ToDictionary(u => u.Id);
foreach(var modifiedUser in modifiedUsers)
{
    User user;
    if (!usersById.TryGetValue(modifiedUser.Id, out user);)
       continue; // or throw

    user.FirstName = modifiedUser.FirstName; // etc  
}

Further improvements - you can use some library like AutoMapper to do the mapping automatically.

Upvotes: 2

Related Questions