Reputation: 7719
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
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
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
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
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
Reputation: 6016
Try this:
var lines = Enumerable.Range(0, int.MaxValue).Select(x => Console.ReadLine()).TakeWhile(x => !string.IsNullOrEmpty(x)).ToList();
Upvotes: 2