Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# - Merge list items into one item based on some matching values

Tags:

c#

merge

list

linq

I have a list of items (List<Tasks> tasks), like this one:

Id                     Action               Source       Target
-------               ---------            --------     ---------
1                      Save                 12           18
4                      Save                 18           21
7                      Save                 21           23
6                      Save                 23           25
10                     Save                 25           27
16                     Save                 29           31
0                      Edit                 31           37

What I want to do, is to merge the rows that have the same (Source and Target) and the same Action. For example, what I need at the end should look like this:

Id                     Action               Source       Target
-------               ---------            --------     ---------
22                     Save                 12           27
16                     Save                 29           31
0                      Edit                 31           37

Which means, all the items that have the same Action(in my case here Save) should be merged to one row/item but only in case the Target value of the upper item is equal to the Source value of the follower item. A follower is the lower item.

For example, upper is item Id = 1 and lower/follower is item Id = 4. So the record that follows.

Is there any way to do this with linq avoiding the too many foreach loops? Maybe something like Hierarchies with CTE in SQL. But I'm still not finding the correct syntax, so that's why I didn't paste any code.

Thanks in advance!

like image 434
alaa_sayegh Avatar asked Oct 16 '22 20:10

alaa_sayegh


1 Answers

If you want "LINQ" solution, you can use Aggregate like this:

var result = tasks.Aggregate(new List<Item>(), (acc, current) =>
{
    if (acc.Count > 0)
    {
        var prev = acc[acc.Count - 1];
        if (prev.Action == current.Action && prev.Target == current.Source)
        {
            // update previous target
            prev.Target = current.Target;
        }
        // otherwise just add
        else acc.Add(current);
    }
    else acc.Add(current);
    return acc;
});

It starts with empty List as accumulator, and feeds items one by one. Then we just add items to accumulator if they do not match criteria, and if they do match - we update previous item instead.

like image 182
Evk Avatar answered Oct 27 '22 00:10

Evk