7

My object hierarchy is like this:

class Detail
{
    public List<Row> Rows { get; set; }
}

class Row
{
    public List<Column> Columns { get; set; }
}

class Column
{
    public string Name { get; set; }
    public bool IsUpdated { get; set; }
}

I want to set column.IsUpdated = true where column.Name = "id". I am trying this, it doesn't work.

detail.Rows.ForEach(r => r.Columns.Where(c => c.Name.ToLower().Equals("id")).ToList<Column>().Select(c => c.IsUpdated = true));
coder
  • 4,121
  • 14
  • 53
  • 88

5 Answers5

9

The philosophy of LINQ is to not have side effects. That's why on purpose it does not make it easy for you to do this. You could do it either with a classic

var cols = details.Rows.SelectMany(r => r.Columns)
                       .Where(c => c.Name.ToLower().Equals("id"));
foreach(var col in cols) {
    col.IsUpdated = true;
}

or by using List.ForEach, but in a different manner:

details.Rows.SelectMany(r => r.Columns)
            .Where(c => c.Name.ToLower().Equals("id")).ToList()
            .ForEach(c => { c.IsUpdated = true });
Ardalan Shahgholi
  • 11,967
  • 21
  • 108
  • 144
Jon
  • 428,835
  • 81
  • 738
  • 806
  • Hey John. So what do you do if there is thousands of rows that you need to update for some reason? lets say the operator clicks on a button and then you have to update all the rows that has status "x" to status "Y" which is easy and fast by normal SQL. right? But if you load everything in memory, it takes forever :( should I write a StoredProcedure instead? is there a better way? SQL: update tblData set status='y' where status='x' – Ashkan S Dec 11 '17 at 09:00
1

LINQ is really intended for querying data, not changing values within the data. If you want to make an entire new detail item, you could do that:

var newDetail = new Detail 
    {
        Rows = detail.Rows.Select(r => new Row 
                                            {
                                                 Columns = r.Columns.Select(c => new Column { Name = c.Name, IsUpdated = c.Name.ToLower() == "id" ? true : c.IsUpdated }).ToList() 
                                            })
                          .ToList() 
    };

Note that the above would be cleaner, most likely, if you added constructors for your types, as well.

That being said, if you want to update it in place, like you were showing, I would just use loops:

foreach(var row in detail.Rows)
    foreach(var col in row.Columns)
        if (col.Name.ToLower() == "id")
            col.IsUpdated = true;

I find that far easier to follow, especially in this case.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
0

Give this a try. This should update "IsUpdated" now with the same concept you were already trying to use.

detail.Rows.ForEach(r => r.Columns.Where(c => c.Name.ToLower().Equals("id")).ToList<Column>().Select(c => {c.IsUpdated = true; return c;}));
0

You shouldn't mutate all elements of a collection with LINQ (although it can be done, see this question). It's simpler and more readable to just use a vanilla foreach.

foreach (var row in detail.Rows)
    foreach (var col in row.Columns)
        if (c.Name.ToLower().Equals("id"))
            c.IsUpdated = true;
Community
  • 1
  • 1
mellamokb
  • 56,094
  • 12
  • 110
  • 136
0

I just got it to work like this. Is it inefficient? Instead of .Select I put .Any. It works, but I'm not sure if it's inefficient on a large data. If yes, I can go with one of the answers.

detail.Rows.ForEach(r => r.Columns.Where(c => c.Name.ToLower().Equals("id")).ToList<Column>().Any(c => c.IsUpdated = true));
coder
  • 4,121
  • 14
  • 53
  • 88
  • 1
    It didn't work because Select() is lazely evaluated, and Any() is greedy. This is a really really bad idea either way! Side effects from Linq Queries are *worst practice* Oh, and edit your question, don't post answers to add information. – asawyer Mar 28 '12 at 22:11