Toto
Toto

Reputation: 7719

While loop to linq

Is there any way to accumulate something using linq.

Initial code :

Something oneItem;
List<Something> allItems;
while ((oneLine = _generator.GenerateSomething()) != null)
    allItems.Add(_generator.CurrentItem);

I would like something like :

var allItems = Enumerable.Take( ()=>_generator.GenerateSomething()).While(item=>item !=null).ToList();

Actually, it would have been very good if generator implemented IEnumerable, I would have use it this way :

var allItems = _generator.TakeWhile(item !=null);

This last one is really easy to understand, I would like to approach it (I can use a kind of wrapper that generate a machine state given a production method (_generator.GenerateSomething()) and a stop condition (item == null). But I can not write this additional class for some reason).

Upvotes: 2

Views: 10507

Answers (5)

Steven
Steven

Reputation: 172786

You can create an iterator method like this:

static IEnumerable<string> ReadAllLines()
{
    string line;
    while ((line = Console.ReadLine()) != string.Empty)
        yield return line;
}

And this way you can stream it or call ToList() on it:

ReadAllLines().ToList();

Or filter on it:

ReadAllLines().Where(line => line.Contains("cool"));

Upvotes: 2

Dirk
Dirk

Reputation: 10968

And just to have yet another approach:

public static class FuncExtensions
{
    public static IEnumerabley<T> AsGenerator(this Func<T> func)
    {
        while(true) yield return func();
    }
}

Usage:

Func<string> f = Console.ReadLine;

f.AsGenerator().Select(...).ToList();

// this won't work unfortunately
Console.ReadLine.AsGenerator().Select(...)

Upvotes: 0

Heinzi
Heinzi

Reputation: 172380

Yes, it's possible by exploiting deferred execution, but I doubt that it is more readable than your original solution:

var lines = (from _ in Enumerable.Range(0, int.MaxValue)
             select Console.ReadLine())
            .TakeWhile(s => s != null)
            .ToList();

(It works because LINQ statements are not executed immediately and TakeWhile ensures that iteration stops in time.)

Upvotes: 0

Servy
Servy

Reputation: 203827

Write a function that is an equivalent to File.ReadLines in concept. Abstract away the code for reading lines from the console once, so that it can be reused.

public static IEnumerable<string> ReadLinesFromConsole()
{
    while (true)
    {
        var next = Console.ReadLine();
        if (next == null)
            yield break;
        yield return next;
    }
}

That said, if you really want to generalize it, you can. What you have here is a simple generator accepting a function.

public static IEnumerable<T> Generate<T>(Func<T> generator)
{
    while (true)
        yield return generator();
}

This allows you to write the code that you had in your example:

var allLines = Generate(() => Console.ReadLine())
    .TakeWhile(line => line != null);

Upvotes: 7

brz
brz

Reputation: 6016

Try this:

var lines = Enumerable.Range(0, int.MaxValue).Select(x => Console.ReadLine()).TakeWhile(x => !string.IsNullOrEmpty(x)).ToList();

Upvotes: 2

Related Questions