Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Selecting entries according to running total

Tags:

c#

linq

I would like to select from a list of files only so many files that their total size does not exceed a threshold (i.e. the amount of free space on the target drive).

I understand that I could do this by adding up file sizes in a loop until I hit the threshold and then use that number to select files from the list. However, is it possible to do that with a LINQ-query instead?

like image 836
Jonas Avatar asked Dec 11 '22 10:12

Jonas


2 Answers

This could work (files is a List<FileInfo>):

var availableSpace = DriveInfo.GetDrives()
    .First(d => d.Name == @"C:\").AvailableFreeSpace;
long usedSpace = 0;
var availableFiles = files
    .TakeWhile(f => (usedSpace += f.Length) < availableSpace);
foreach (FileInfo file in availableFiles)
{
    Console.WriteLine(file.Name);
}
like image 55
Tim Schmelter Avatar answered Dec 13 '22 22:12

Tim Schmelter


You can achieve that by using a closure:

var directory = new DirectoryInfo(@"c:\temp");
var files = directory .GetFiles();

long maxTotalSize = 2000000;

long aggregatedSize = 0;
var result = files.TakeWhile(fileInfo => 
{
    aggregatedSize += fileInfo.Length;
    return aggregatedSize <= maxTotalSize;
});

Theres a caveat though, because the variable aggregatedSize may get modified after you have left the scope where it has been defined.

You could wrap that in an extension method though - that would eliminate the closure:

public static IEnumerable<FileInfo> GetWithMaxAggregatedSize(this IEnumerable<FileInfo> files, long maxTotalSize)
{
    long aggregatedSize = 0;
    return files.TakeWhile(fileInfo => 
    {
        aggregatedSize += fileInfo.Length;
        return aggregatedSize <= maxTotalSize;
    });
}

You finally use the method like this:

var directory = new DirectoryInfo(@"c:\temp");
var files = directory.GetFiles().GetWithMaxAggregatedSize(2000000);

EDIT: I replaced the Where-method with the TakeWhile-method. The TakeWhile-extension will stop once the threshold has been reached, while the Where-extension will continue. Credits for bringing up the TakeWhile-extension go to Tim Schmelter.

like image 34
Spontifixus Avatar answered Dec 14 '22 00:12

Spontifixus