2

This should be simple but i am not able to figure this out. I have a Datatable that has Name, Age where there are rows that have student names, but then multiple rows of special students' Name 'Chris's, and John's. the end result should have a table with all the rows except Chris and John to be combined into one row called Other with the sum of their age.

ie.

NAME | AGE
Tom | 20
John | 15
Peter | 5
John | 2
Tom | 33
Chris | 20

End Result:

NAME | AGE
Tom | 20
Peter | 5
Tom | 33
Other | 37

This is what im doing right now:

var others = table.AsEnumerable().Where(x => x["NAME"].ToString().Equals("Chris") || x["NAME"].ToString().Equals("John"));
var tmpOthers = 0;
foreach (DataRow otherRow in others)
            tmpOthers += decimal.Parse(otherRow["AGE"].ToString());
table = table.AsEnumerable().Where(x => !x["NAME"].ToString().Equals("Chris") || !x["NAME"].ToString().Equals("John")).CopyToDataTable();
table.Add(//OTHER-ROW-Here)

This works but i know there must be an easier way to do this in a single LiNQ statement. also i could not put the table.Rows.Remove in the same for loop because of iteration change.

2

2 Answers 2

3

I'd do that using Linq + delegate. Why? A solution with delegate is more elegant and more specific for such of case, because a name may starts with big or small letter. Check below solution:

//delegate to return "Other" for {"Chris","John"} - ignoring case
Func<string, string> GetOtherName = delegate(string s)
    {
        if(string.Equals(s, "Chris", StringComparison.OrdinalIgnoreCase) ||
                string.Equals(s, "John", StringComparison.OrdinalIgnoreCase))
            return "Other";
        else
            return s;
    };

//get total age for "Other"
var otherAge = dt.AsEnumerable()
            .Where(x=>GetOtherName(x.Field<string>("NAME")) == "Other")
            .Select(x=>x.Field<int>("AGE")).Sum();

//remove "Other" rows
dt.AsEnumerable()
    .Where(x=>GetOtherName(x.Field<string>("NAME")) == "Other")
    .ToList()
    .ForEach(r=>r.Delete());

//finally add "other" row
dt.Rows.Add(new object[]{"Other", otherAge});

Result:

NAME  AGE
Tom   20 
Peter 5 
Tom   33 
Other 37 

For further details, please see: Func Delegate

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

1 Comment

Super! This is so much cleaner! Love the coding style
1

Well you can use a for loop for that purpose like

for(int i=0; i<others.Count(); i++)
    table.Rows.Remove(others[i]);

6 Comments

It works with a for loop but the code i gave is a little messy and i know there should be a way to write all of that logic into one single LiNQ statement. Something like table.AsEnumerable().Where(x => x["NAME"].ToString().Equals("Chris") || x["NAME"].ToString().Equals("John")).(sum up the AGE in some local variable).Removes these rows.CopyToTable() a logic like that
@ civic.sir In my opinion that giant linq statement is messier than your original code. You could do a .ForEach() on the .Where() result set, but I'm not sure how to RemoveRange on that Where() again after that.
@dckuehn Yes that is what im looking for .. .ForEach().Where and then I can simply do a copytoDataTable() at the end no?
@civic.sir I don't think you can have a single LINQ statement that both adds a new entry to the list, and removes entities at the same time. Even if you can, I think that would be messier than your current solution.
Ahh i see. kk makes sense Thanks a lot
|

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.