Home > OS >  How to merge two lists of objects based on a property?
How to merge two lists of objects based on a property?

Time:10-11

I have the two lists below where one comes from the database and the other from a JSON. The one from the database has it's Id associated.

List from database:

EmployeeId FirstName LastName EmployeeNumber
1234        Tom       Cruise    98372829
5555        James     Bond      93932228

The employeeId is a GUID Saved in the database.

Now I retrieve a list of Employees again to detect changes - James Bond Lastname changed. And I used automapper to map in the same format as my database entity.

List from JSON:

EmployeeId FirstName LastName EmployeeNumber
000-0000... Tom       Cruise    98372829
000-0000... James     Carter    93932228

Now I want to update the first list with the FirstName and LastName based on the EmployeeNumber.

// Employees retrieved in JSON
var retrievedEmployees = JsonSerializer.Deserialize<List<EmployeeDto>>(methodToRetrieveEmployees()))!.ToList();

var mappedEmployees = _mapper.Map<IEnumerable<Employee>>(retrievedEmployees);

var existingEmployeeFromDatabase = await GetExistingEmployees();

var employeesWithLatestUpdates = mappedEmployees
    .Where(y => existingEmployeeFromDatabase.Any(z => z.Number == y.Number)).ToList();

So What I need to do is to update employeeswithLatestChanges (Id,FirstName and LastName) with the values from existingEmployees from the database. Since they don't have Id, this should be mapped by the EmployeeNumber.

I have tried to use Union/joins but no luck.

CodePudding user response:

Updating by linq in c# 6

var updatedEmployee = employeeswithLatestChanges.Select(x => new Employee
{
   FirstName = existingEmployees.FirstOrDefault(y => y.EmployeeId == x.EmployeeNumber)?.FirstName?? x.FirstName,
   LastName = existingEmployees.FirstOrDefault(y => y.LastName == x.code)?.LastName ?? x.LastName ,
});

Can Use Loop also

foreach (var dbEmp in existingEmployees)
{
     foreach(var emp in (employeeswithLatestChanges.Where(t => t.EmployeeNumber == dbEmp.EmployeeId)))
     {
        emp.FirstName= dbEmp.FirstName;
        emp.LastName= dbEmp.LastName;
     }
}

CodePudding user response:

To fix the idea we can assume the following class to represent an employee:

public sealed class Employee  
{
  public string FirstName { get; set; }
  public string LastName { get; set; }
  public Guid Id { get; set; }
  public int Number { get; set; }
}

Suppose you have a collection of employees named employees that you want to update by using another collection of employees named updatedEmployees:

IEnumerable<Employee> employees = ....;
IEnumerable<Employee> updatedEmployees = ....;

The simplest way I can think of to solve your problem is the following:

public static void Main(string[] args)
{
  // previous code omitted for brevity

  Dictionary<int, Employee> employeeNumberToUpdatedEmployee = updatedEmployees.ToDictionary(x => x.Number);

  foreach (var employee in employees) 
  {
    if (employeeNumberToUpdatedEmployee.TryGetValue(employee.Number, out var updatedEmployee)
    {
      employee.FirstName = updatedEmployee.FirstName;
      employee.LastName = updatedEmployee.LastName;
    }
  }

  // subsequent code omitted for brevity
}

An alternative way to solve this problem is to perform a join operation by using LINQ to objects, as in the following code:

var employees = new List<Employee>
{
  new Employee{ Id = Guid.NewGuid(), Number = 11, FirstName = "Bob", LastName = "Red"  },
  new Employee{ Id = Guid.NewGuid(), Number = 13, FirstName = "Alice", LastName = "Smith"  },
  new Employee{ Id = Guid.NewGuid(), Number = 5, FirstName = "Max", LastName = "Brown"  },
};

var updatedEmployees = new List<Employee>
{
  new Employee{ Id = Guid.NewGuid(), Number = 11, FirstName = "Bob", LastName = "Verdi"  },
  new Employee{ Id = Guid.NewGuid(), Number = 13, FirstName = "Alice", LastName = "Rossi"  },
  new Employee{ Id = Guid.NewGuid(), Number = 78, FirstName = "Sam", LastName = "Smith"  },
};

// here we are using the fact that we can have, at most, one match
var query = from employee in employees
            join updatedEmployee in updatedEmployees on employee.Number equals updatedEmployee.Number into matches
            from match in matches.DefaultIfEmpty()
            select new Employee
            {
              Id = employee.Id,
              Number = employee.Number,
              FirstName = match?.FirstName ?? employee.FirstName,
              LastName = match?.LastName ?? employee.LastName,
            };

foreach (var item in query)
{
  Console.WriteLine($"{item.FirstName} {item.LastName}");
}
  • Related