## digitalmars.D.learn - Output range of ranges to single buffer

Jacob Carlborg <doob me.com> writes:
```Is it possible to somehow output a range of ranges to a single string
buffer? For example, converting an array of integers to a string with
the same representation as the source code.

import std.algorithm;
import std.conv;
import std.string;

void main()
{
auto a = [1, 2, 3, 4, 5];
auto b = '[' ~ a.map!(e => e.to!string).join(", ") ~ ']';
assert(b == "[1, 2, 3, 4, 5]");
}

The above code is straight forward. But I would like to avoid creating
the intermediate strings, "e.to!string", and instead put the converted
integer and the result of join directly in the same buffer.

A bit more complex example:

struct Foo
{
int a;
string b;
int c;

string toString()
{
auto values = [a.to!string, b, c.to!string].join(", ");
return "Foo(" ~ values ~ ')';
}
}

void main()
{
auto a = [
Foo(1, "foo", 2),
Foo(3, "bar", 4)
];

auto b = '[' ~ a.map!(e => e.to!string).join(", ") ~ ']';
assert(b == "[Foo(1, foo, 2), Foo(3, bar, 4)]");
}

--
/Jacob Carlborg
```
Jan 13 2016
Dav1d <d dav1d.de> writes:
```On Wednesday, 13 January 2016 at 21:15:03 UTC, Jacob Carlborg
wrote:
Is it possible to somehow output a range of ranges to a single
string buffer? For example, converting an array of integers to
a string with the same representation as the source code.

[...]

std.format can do it. From the site:

import std.stdio;

void main()
{
writefln("My items are %(%s %).", [1,2,3]);
writefln("My items are %(%s, %).", [1,2,3]);
}
```
Jan 13 2016
"H. S. Teoh via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com> writes:
```On Wed, Jan 13, 2016 at 10:15:03PM +0100, Jacob Carlborg via
Digitalmars-d-learn wrote:
Is it possible to somehow output a range of ranges to a single string
buffer? For example, converting an array of integers to a string with
the same representation as the source code.

import std.algorithm;
import std.conv;
import std.string;

void main()
{
auto a = [1, 2, 3, 4, 5];
auto b = '[' ~ a.map!(e => e.to!string).join(", ") ~ ']';
assert(b == "[1, 2, 3, 4, 5]");
}

The above code is straight forward. But I would like to avoid creating
the intermediate strings, "e.to!string", and instead put the converted
integer and the result of join directly in the same buffer.

Isn't that just a matter of replacing each of the segments with their
range equivalents? Also, std.format.formattedWrite will do
writeln-formatting into a buffer (well, any output range, really) -- I'm
pretty sure it doesn't allocate, at least for the simplest cases like
converting an integer. So you should be able to do something like this:

auto data = [ 1, 2, 3, 4, 5 ];
char[] buf = ...;
formattedWrite(buf, "[%(%d, %)]", data);

T

--
Customer support: the art of getting your clients to pay for your own
incompetence.
```
Jan 13 2016
=?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
```On 01/13/2016 01:20 PM, H. S. Teoh via Digitalmars-d-learn wrote:

std.format.formattedWrite will do
writeln-formatting into a buffer (well, any output range, really) -- I'm
pretty sure it doesn't allocate, at least for the simplest cases like
converting an integer. So you should be able to do something like this:

auto data = [ 1, 2, 3, 4, 5 ];
char[] buf = ...;
formattedWrite(buf, "[%(%d, %)]", data);

And buf can be an Appender:

import std.stdio;
import std.format;
import std.array;

void main() {
auto data = [ 1, 2, 3, 4, 5 ];

auto buf = appender!string(); // Or appender!(char[]) if needed
formattedWrite(buf, "[%(%d, %)]", data);

assert(buf.data == "[1, 2, 3, 4, 5]");
}

Ali
```
Jan 13 2016
Jacob Carlborg <doob me.com> writes:
```On 2016-01-13 22:20, H. S. Teoh via Digitalmars-d-learn wrote:

Isn't that just a matter of replacing each of the segments with their
range equivalents? Also, std.format.formattedWrite will do
writeln-formatting into a buffer (well, any output range, really) -- I'm
pretty sure it doesn't allocate, at least for the simplest cases like
converting an integer. So you should be able to do something like this:

auto data = [ 1, 2, 3, 4, 5 ];
char[] buf = ...;
formattedWrite(buf, "[%(%d, %)]", data);

Aha, interesting. I didn't know formattedWrite could format an
array/range directly like that.

The more complex example works by defining "toString" which takes an
output range (delegate). But what if I need to format a third party type
that I cannot add methods to? UFCS does not seem to work.

--
/Jacob Carlborg
```
Jan 13 2016
=?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
```On 01/13/2016 11:41 PM, Jacob Carlborg wrote:

what if I need to format a third party type that I cannot add
methods to? UFCS does not seem to work.

Here is an experiment that wraps the third party type to provide a lazy
toString:

import std.stdio;
import std.format;
import std.array;
import std.algorithm;
import std.range;

/* Wraps an element and provides a lazy toString that dispatches the
work to a
* user-provided 'formatter' function. E is the element type. */
struct Formatted(alias formatter, E) {
E element;

void toString(void delegate(const(char)[]) sink) const {
formatter(sink, element);
}
}

/* Adapts a range by converting the elements to 'Formatted'. R is the range
* type. */
auto formatted(alias formatter, R)(R range) {
return range.map!(e => Formatted!(formatter, ElementType!R)(e));
}

/* A third party test type that does not have a lazy toString member
* function. */
struct Foo {
double d;
string s;
}

void main() {
auto data = [ Foo(1.5, "hello"), Foo(2.5, "world") ];

auto buf = appender!string();
formattedWrite(buf, "%(%s\n%)",
data.formatted!(
(sink, a) => formattedWrite(sink, "%s and %s",
a.d, a.s)));

writeln(buf.data);
}

Prints the objects according to the user's lambda:

1.5 and hello
2.5 and world

It would be great if the user could provide just the format and accessed
the members:

data.formatted!("%s and %s", a.d, a.s); // <-- ERROR

But I couldn't get it working because the compiler does not know what
'a' is at that point. It might be acceptable to provide a lambda per
member but then it gets to cluttered:

data.formatted!("%s and %s", a => a.d, a => a.s); // Might work

Ali
```
Jan 14 2016
Jacob Carlborg <doob me.com> writes:
```On 2016-01-14 17:59, Ali Ã‡ehreli wrote:

Here is an experiment that wraps the third party type to provide a lazy
toString:

import std.stdio;
import std.format;
import std.array;
import std.algorithm;
import std.range;

/* Wraps an element and provides a lazy toString that dispatches the
work to a
* user-provided 'formatter' function. E is the element type. */
struct Formatted(alias formatter, E) {
E element;

void toString(void delegate(const(char)[]) sink) const {
formatter(sink, element);
}
}

Wrap the object, why didn't I think of that :)

/* Adapts a range by converting the elements to 'Formatted'. R is the range
* type. */
auto formatted(alias formatter, R)(R range) {
return range.map!(e => Formatted!(formatter, ElementType!R)(e));
}

/* A third party test type that does not have a lazy toString member
* function. */
struct Foo {
double d;
string s;
}

void main() {
auto data = [ Foo(1.5, "hello"), Foo(2.5, "world") ];

auto buf = appender!string();
formattedWrite(buf, "%(%s\n%)",
data.formatted!(
(sink, a) => formattedWrite(sink, "%s and %s",
a.d, a.s)));

writeln(buf.data);
}

Prints the objects according to the user's lambda:

1.5 and hello
2.5 and world

Thanks.

--
/Jacob Carlborg
```
Jan 14 2016
Jonathan M Davis via Digitalmars-d-learn writes:
```On Thursday, January 14, 2016 08:41:23 Jacob Carlborg via Digitalmars-d-learn
wrote:
On 2016-01-13 22:20, H. S. Teoh via Digitalmars-d-learn wrote:

Isn't that just a matter of replacing each of the segments with their
range equivalents? Also, std.format.formattedWrite will do
writeln-formatting into a buffer (well, any output range, really) -- I'm
pretty sure it doesn't allocate, at least for the simplest cases like
converting an integer. So you should be able to do something like this:

auto data = [ 1, 2, 3, 4, 5 ];
char[] buf = ...;
formattedWrite(buf, "[%(%d, %)]", data);

Aha, interesting. I didn't know formattedWrite could format an
array/range directly like that.

The more complex example works by defining "toString" which takes an
output range (delegate). But what if I need to format a third party type
that I cannot add methods to? UFCS does not seem to work.

You can't do toString via UFCS any more than you can overload any operators
via UFCS. If a type's toString does not work how you want, or a type does
not provide one, then you'll need to convert objects of that type to a
string in a different way.

- Jonathan M Davis
```
Jan 14 2016