I am trying to get the contents of a directory, sorted by modified time. I don't think there is a way to do this directly within the dirEntries
call, so my strategy is to collect all file times and names, then sort the two arrays in lock-step.
Problem 1: I can't figure out how to convert a sysTime
into an integer.
Problem 2: I can't figure out how to sort two arrays in parallel.
As with every problem in D, it's impossible to find out how to do this :(
Here is my code:
import std.file;
import std.stdio : writeln;
import std.algorithm;
import std.datetime;
void main() {
string[] myFiles;
double[] myTimes;
foreach (DirEntry e; dirEntries("c:/users/istaffel/", SpanMode.shallow)) {
// calculate unix time (this doesn't work)
auto dur = (cast(Date)e.timeLastModified) - Date(1970,1,1);
// store modified time and filename
myTimes ~= dur.seconds;
myFiles ~= e.name;
}
// now find a way to sort myFiles in order of ascending myTimes...
// print in order
for (int i = 0; i < myTimes.length; i++) {
writeln(myTimes[i], " ", myFiles[i]);
}
}
In general, unless you're interacting with C, I would suggest that converting SysTime
to an integral value is a bad idea (and that converting it to double
is even worse). But if you really need to convert it to time_t
like you seem to be doing, then just use SysTime
's toUnixTime
function:
auto timeT = e.timeLastModified.toUnixTime();
The simplest solution to do what you're trying to do would be something like
import std.algorithm;
import std.array;
import std.datetime;
import std.file;
import std.stdio;
void main(string[] args)
{
auto directoryToList = args[1];
auto files = array(dirEntries(directoryToList, SpanMode.shallow));
sort!"a.timeLastModified < b.timeLastModified"(files);
foreach(file; files)
writefln("%s %s", file.timeLastModified, file.name);
}
And if you really want a time_t
, then make it file.timeLastModified.toUnixTime()
.
dirEntries
returns a range, so you can just iterate it and operate on it directly, but in order to sort it, you need a random-access range (which the result of dirEntries
isn't, since it accesses the files lazily). So, you can use std.array.array
to create an array from it, and then sort that. sort
takes a predicate (be it a string converted to a function with std.functional, a lambda literal, a delegate, a function pointer, or anything callable with two arguments of the type that you're sorting - in this case I used a string, and in the example below, I use a lambda literal for map
).
If you're dealing with a lot of files and want to minimize the amount of memory used (as DirEntry
does have several member variables, and so an array of them all may take up more more memory than you'd like if all you care about is the files' names and modifications times), then it gets more entertaining to do, but it's still quite feasible.
import std.algorithm;
import std.array;
import std.datetime;
import std.file;
import std.stdio;
import std.typecons;
void main(string[] args)
{
auto directoryToList = args[1];
auto files = dirEntries(directoryToList, SpanMode.shallow);
auto pairs = array(map!(a => tuple(a.timeLastModified, a.name))(files));
sort!"a[0] < b[0]"(pairs);
foreach(pair; pairs)
writefln("%s: %s", pair[0], pair[1]);
}
And again, if you really want a time_t
, just make that pair[0].toUnixTime()
.
This ends up creating an array of Tuples which contain just the time and name, and those then get sorted using just the time.
If you aren't very familiar with ranges, then I would suggest that you read this chapter from an online book on D. D's standard library uses ranges quite heavily, which may be why you feel like you have a hard time figuring out how to do things in D. Ranges are an extremely powerful concept, but they do take some getting used to.