Skip to content

Instantly share code, notes, and snippets.

@Biotronic
Last active December 26, 2017 02:37
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Biotronic/0bc6048b880d67bfdca970453cc47cf9 to your computer and use it in GitHub Desktop.
Save Biotronic/0bc6048b880d67bfdca970453cc47cf9 to your computer and use it in GitHub Desktop.
Expressive C++ challenge in D
/*
Expressive C++17 challenge, in D
http://www.bfilipek.com/2017/09/the-expressive-cpp17-challenge.html
D features used:
Ranges (MmText, zip, map)
UFCS (all over)
String mixins (in find)
Wysiwyg strings
Scoped imports
Selective imports
Built-in unit tests
@safe and @trusted
Import expressions
*/
import std.range : zip, array, front;
import std.algorithm : map, splitter, canFind, find, min;
import std.format : format;
@safe:
struct MmText {
import std.mmfile : MmFile;
import std.utf : decodeFront;
ulong _fileOffset;
MmFile _file;
@trusted
this(string filename) {
_file = new MmFile(filename);
}
dchar front() {
auto data = nextSlice;
return data.decodeFront;
}
void popFront() {
auto data = nextSlice;
size_t bytes;
data.decodeFront(bytes);
_fileOffset += bytes;
}
bool empty() {
return ulLength <= 0;
}
@trusted
ulong ulLength() const {
return _file.length - _fileOffset;
}
@property @trusted
private string nextSlice() {
auto offset = min(4, ulLength);
return cast(string)_file[_fileOffset.._fileOffset+offset];
}
} unittest {
import std.algorithm.comparison : equal;
auto expected = import(__FILE__);
auto result = MmText(__FILE__);
assert(equal(result, expected));
}
struct Record {
import std.typecons : Tuple;
Tuple!(string, `name`, string, `value`)[] payload;
this(string[] headers, string[] values) {
assert(headers.length == values.length);
payload = headers.zip(values).map!(typeof(payload[0])).array;
}
ref string opIndex(string column) {
return payload.find!`a.name == b`(column).front.value;
} unittest {
auto r = Record([`a`],[`b`]);
assert(r[`a`] == `b`);
r[`a`] = `c`;
assert(r[`a`] == `c`);
}
string toString() const {
return format(`%-(%s,%)`, payload.map!(a => escape(a.value)));
} unittest {
auto r = Record([`a`],[`b`]);
assert(r.toString == `b`);
r = Record([`a`,`b`],[`c`,`d`]);
assert(r.toString == `c,d`);
r = Record([`a`,`b`],[`c`,`d,e`]);
assert(r.toString == `c,"d,e"`);
}
}
string escape(string s) {
import std.string : replace;
if (s.canFind(`"`, `,`))
return `"%s"`.format(s.replace(`"`, `""`));
return s;
} unittest {
assert(``.escape == ``);
assert(`a`.escape == `a`);
assert(`,`.escape == `","`);
assert(`"`.escape == `""""`);
assert(`",`.escape == `""","`);
}
void main(string[] args) {
import std.stdio : writefln, writeln;
import std.file : readText;
import std.csv : csvReader;
if (args.length < 4) {
writeln(`Please provide input file, column, and replacement.`);
return;
}
const filename = args[1];
const column = args[2];
const replacement = args[3];
auto data = MmText(filename).csvReader!string;
auto headers = data.front.array;
data.popFront();
writefln(`%-(%s,%)`, headers);
writefln("%-(%s\n%)", data
.map!(a => Record(headers, a.array))
.map!(a => (a[column] = replacement, a)));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment