QazPhilby
QazPhilby

Reputation: 117

Updating a System.Linq.Enumerable after the query

I have a query where I load data from a csv file

var transactions = from line in System.IO.File.ReadAllLines(path).Skip(1)
    let columns = line.Split(',')
    select new Transaction
    {
        AccountNo = long.Parse(columns[0]),
        Date = DateTime.Parse(columns[1], new CultureInfo("en-AU")),
        DebitAmount = columns[3].Length > 0? decimal.Parse(columns[3]): 0.00m,
        CreditAmount = columns[4].Length > 0 ? decimal.Parse(columns[4]) : 0.00m,
        Categories = columns[5],
        Serial = columns[6]
    };

Now when i iterate through this enumerable i want to be able to set other fields of the transaction object.

With a List I could use a for loop to loop through each one and make any changes as required, but how can you do this using a System.Linq.Enumerable?

Upvotes: 1

Views: 166

Answers (3)

Hossain Muctadir
Hossain Muctadir

Reputation: 3626

        var transactions =
            System.IO.File.ReadAllLines("").Skip(1).Select(line => new { line, columns = line.Split(',') }).Select(
                strings => new Transaction
                          {
                              AccountNo = long.Parse(strings.columns[0]),
                              Date = DateTime.Parse(strings.columns[1], new CultureInfo("en-AU")),
                              DebitAmount = strings.columns[3].Length > 0 ? decimal.Parse(strings.columns[3]) : 0.00m,
                              CreditAmount = strings.columns[4].Length > 0 ? decimal.Parse(strings.columns[4]) : 0.00m,
                              Categories = strings.columns[5],
                              Serial = strings.columns[6]
                          }).ToList();

        IEnumerable<Transaction> newTransactions = transactions.Select(tr => new Transaction(){/*assign new values*/});

Without creating new object

    transactions.ForEach(transaction => { transaction.AccountNo = 12; /*other manipulation*/});

Upvotes: 0

Jeroen van Langen
Jeroen van Langen

Reputation: 22073

You can convert it to a List<Transaction> with:

var transactions = (from line in System.IO.File.ReadAllLines(path).Skip(1)
    let columns = line.Split(',')
    select new Transaction
    {
        AccountNo = long.Parse(columns[0]),
        Date = DateTime.Parse(columns[1], new CultureInfo("en-AU")),
        DebitAmount = columns[3].Length > 0? decimal.Parse(columns[3]): 0.00m,
        CreditAmount = columns[4].Length > 0 ? decimal.Parse(columns[4]) : 0.00m,
        Categories = columns[5],
        Serial = columns[6]
    }).ToList();

If you don't materialize the query, new instances of Transaction will be created on iterating the results. The ToList() will iterate the query and materialize the results into a list. So you can change the results.

Look here for more info: How to: Store the Results of a Query in Memory http://msdn.microsoft.com/en-us/library/bb513810.aspx

Upvotes: 1

Steven
Steven

Reputation: 172826

To be able to do this you must first 'materialize' the query to an in-memory collection, such as array:

transactions = transactions.ToArray();

foreach (var transaction in transactions)
{
    // Fill other members here
}

Because if you don't do this, your changes will be lost, simply because iterating the query will create new Transaction objects all the time.

Another option is to write a 'transformation' method that does this:

public Transaction FillOtherProperties(Transaction transaction)
{
    // Fill other members here

    return transaction;
}

And you can use it as follows:

transactions.Select(FillOtherProperties);

This is useful when you need the iterator to be lazy, for instance when the collection is too big to fit in memory.

Downside of this is that you're mutating existing objects which can be seen as a side effect, which can cause confusion to other developers. You would typically like your queries to be side effect free.

Upvotes: 0

Related Questions