Feature to_string() or equivalent for aggregates

Theraze

Active member
Well, I was noting mostly that the print function kills line breaks, while the print_html function allows them. :)
 

slyz

Developer
The problem is that it doesn't properly go through all the dimensions in the map to print them all. That's the same problem I'm having with writing a patch. Well, that and that I'm extremely inexperienced with java.
I thought that a file like this:
Code:
Giant's Castle	Disco Bandit	5
Giant's Castle	Pastamancer	10
Hobopolis Town Square	Sauceror	20
Outskirts of The Knob	Pastamancer	10
would fit into a string [ string ] map with the 2nd and 3rd entry grouped together like this:
Code:
temp[ "Giant's Castle" ] = "Disco Bandit	5"

The problem is that:

1) the 3rd field is ignored if file_to_map() is used on a string [ string ] map
2) the second "Giant's Castle" entry replaces the first one in a string [ string ] map in this case

So yes, this solution doesn't work for multi-dimensional arrays.
 

heeheehee

Developer
Staff member
IIRC, before the aggregate datatype was implemented, zarqon noted that it was technically possible to accomplish the generalized load_current_map with something like "string [string, string, string, string ... ] map", but he saw it as too great a kludge.
 
Since you'd always (at least, I can't think of a situation in which you wouldn't) know the amount of dimensions your aggregate would have at the time of writing the code, couldn't you pass that number as an argument? It'd still probably be murder writing the code to -handle- that information, but I'm sure someone clever could find a way to construct the string[string, string, ...] with that number.
 

slyz

Developer
Since you'd always (at least, I can't think of a situation in which you wouldn't) know the amount of dimensions your aggregate would have at the time of writing the code, couldn't you pass that number as an argument? It'd still probably be murder writing the code to -handle- that information, but I'm sure someone clever could find a way to construct the string[string, string, ...] with that number.
Here is a function that takes a map that was saved into a temporary file and turns it into a string. The parameters that are passed to it are the name of the temporary file that contains the map and a number indicating how many fields are in the map.

I more or less hacked together a way to do this by building an ASH command in a string (using the size of the aggregate you wish to turn into a string), then by using cli_execute() to make it load the file, build the string, and saving the string back into the temporary file.

It's kind of kludgy, and you can't use \t or \n in the string that is saved back into the file since it would turn a file with 1 line and 2 fields into a file with several lines and/or more than 2 fields.
PHP:
string file_to_string( string file, int dim )
{
	if ( file == "" || dim < 2 )
	{
		print( "usage: print_map( string file, int dim ) - file cannot be empty, dim must be greater than 1", "red" );
		return "";
	}
	string command;
	command += "ashq " ;
	command += "string str; " ;
	command += "string [ ";
	for i from 1 to (dim - 1)
	{
		command += "string";
		if( i < (dim - 1) ) command += ", ";
	}
	command += " ] print_map; ";
	command += "file_to_map( '" + file + "', print_map ); ";
	command += "foreach ";
	for i from 1 to dim
	{
		command += "str" + i;
		if( i < dim ) command += ", ";
	}
	command += " in print_map ";
	command += "str += ";
	for i from 1 to dim
	{
		command += "str" + i + " ";
		if( i < dim ) command += "+ '=>' + ";
	}
	command += "+ '||';";
	command += "string [ int ] save_map; ";
	command += "save_map[ 0 ] = str; ";
	command += "map_to_file( save_map, '" + file + "' );";
	
	cli_execute( command );
	
	string [ int ] load_map;
	file_to_map( file, load_map );
	return load_map[ 0 ];
}

int [ location, class, item ] temp;
temp[ $location[ Giant's Castle ], $class[ Disco Bandit ], $item[ Disco Banjo ] ] = 5 ; 
temp[ $location[ Giant's Castle ], $class[ Pastamancer ], $item[ Shagadelic Disco Banjo ] ] = 10 ; 
temp[ $location[ Outskirts of The Knob ], $class[ Pastamancer ], $item[ disco mask ] ] = 10 ; 
temp[ $location[ Hobopolis Town Square ], $class[ Sauceror ], $item[ disco mask ] ] = 20 ; 

map_to_file( temp, "temp.txt" );
print( file_to_string( "temp.txt", 4 ) );
returns this:
Code:
Giant's Castle=>Disco Bandit=>Disco Banjo=>5||Giant's Castle=>Pastamancer=>Shagadelic Disco Banjo=>10||Hobopolis Town Square=>Sauceror=>disco mask=>20||Outskirts of The Knob=>Pastamancer=>disco mask=>10||
Using
PHP:
print( file_to_string( "temp.txt", 2 ) );
instead returns:
Code:
Giant's Castle=>Pastamancer||Hobopolis Town Square=>Sauceror||Outskirts of The Knob=>Pastamancer||

The code that is build and executed with cli_execute() is:
PHP:
string str;
string [ string, string, string ] print_map;
file_to_map( 'temp.txt', print_map );
foreach str1, str2, str3, str4 in print_map
	str += str1 + '=>' + str2 + '=>' + str3 + '=>' + str4 + '||';
string [ int ] save_map;
save_map[ 0 ] = str;
map_to_file( save_map, 'temp.txt' );
That code varies depending on the name of the temporary file and on the number of dimensions that are passed to file_to_string().
 
Last edited:

slyz

Developer
I'll double post for this second solution, using a KoLMafia property instead of writing the result into a temporary file:
PHP:
string file_to_string( string file, int dim )
{
	if ( file == "" || dim < 2 )
	{
		print( "usage: print_map( string file, int dim ) - file cannot be empty, dim must be greater than 1", "red" );
		return "";
	}
	string command;
	command += "ashq " ;
	command += "string str; " ;
	command += "string [ ";
	for i from 1 to (dim - 1)
	{
		command += "string";
		if( i < (dim - 1) ) command += ", ";
	}
	command += " ] print_map; ";
	command += "file_to_map( '" + file + "', print_map ); ";
	command += "foreach ";
	for i from 1 to dim
	{
		command += "str" + i;
		if( i < dim ) command += ", ";
	}
	command += " in print_map ";
	command += "str += ";
	for i from 1 to dim
	{
		command += "str" + i + " ";
		if( i < dim ) command += "+ '\\t' + ";
	}
	command += "+ '\\n';";
	command += "set_property( 'file2string', str ); ";
	
	cli_execute( command );
	return get_property( 'file2string' );
}

int [ location, class, item ] temp;
temp[ $location[ Giant's Castle ], $class[ Disco Bandit ], $item[ Disco Banjo ] ] = 5 ; 
temp[ $location[ Giant's Castle ], $class[ Pastamancer ], $item[ Shagadelic Disco Banjo ] ] = 10 ; 
temp[ $location[ Outskirts of The Knob ], $class[ Pastamancer ], $item[ disco mask ] ] = 10 ; 
temp[ $location[ Hobopolis Town Square ], $class[ Sauceror ], $item[ disco mask ] ] = 20 ; 

map_to_file( temp, "temp.txt" );
print( file_to_string( "temp.txt", 4 ) );
This time, you can use \t and \n without any problem, and this is what you get:
Code:
> call test.ash

file2string => Giant's Castle Disco Bandit Disco Banjo 5
Giant's Castle Pastamancer Shagadelic Disco Banjo 10
Hobopolis Town Square Sauceror disco mask 20
Outskirts of The Knob Pastamancer disco mask 10
Giant's Castle Disco Bandit Disco Banjo 5Giant's Castle Pastamancer Shagadelic Disco Banjo 10Hobopolis Town Square Sauceror disco mask 20Outskirts of The Knob Pastamancer disco mask 10
The 'file2string' property contains:
Code:
file2string=Giant's Castle\tDisco Bandit\tDisco Banjo\t5\nGiant's Castle\tPastamancer\tShagadelic Disco Banjo\t10\nHobopolis Town Square\tSauceror\tdisco mask\t20\nOutskirts of The Knob\tPastamancer\tdisco mask\t10
 
Last edited:

StDoodle

Minion
Harumph. If file_to_map() is from a built-in Java function, it looks like there's no good recourse for me. I appreciate all of the efforts, but none of these solutions appear promising for use with maps of records. Custom functions for each map it is!
 

slyz

Developer
When you use map_to_file() on a map that contains records, it is saved as if each field of the record was a map key, so this:
PHP:
record {
	int num;
	item itm;
	class cls;
} [ int ] test_rec;

test_rec[ 0 ].num = 1;
test_rec[ 0 ].itm = $item[ big rock ];
test_rec[ 0 ].cls = $class[ disco bandit ];

test_rec[ 1 ].num = 2;
test_rec[ 1 ].itm = $item[ disco banjo ];
test_rec[ 1 ].cls = $class[ pastamancer ];
becomes:
Code:
0	1	big rock	Disco Bandit
1	2	Disco Banjo	Pastamancer
and using my file_to_string() function
PHP:
map_to_file( test_rec, "temp.txt" );
file_to_string( "temp.txt", 4 );
saves this the file2string Mafia property:
Code:
file2string=0\t1\tbig rock\tDisco Bandit\n1\t2\tDisco Banjo\tPastamancer

It was fun to play with this, but since I really don't know what you wish to do with this or how you want the string to be formatted, it's hard to go any further with it.


EDIT: if you use map_to_file( test_rec, "temp.txt", false ), this is the file you get:
Code:
0	num	1
0	itm	big rock
0	cls	Disco Bandit
1	num	2
1	itm	Disco Banjo
1	cls	Pastamancer
So if you need the name of the fields to appear in your string, it's possible too.
 
Last edited:

StDoodle

Minion
Hmm... too late at night (5am my time) to fully analyze that, but for reference what I'm looking for is a format IDENTICAL to the contents of map_to_file()'s output, so I can upload it to a remote server via a mafia script & a small server script.

Edit: further analyzed, wouldn't work for what I'm doing, as some records have fields which are themselves aggregates.
 
Last edited:
Hmm... too late at night (5am my time) to fully analyze that, but for reference what I'm looking for is a format IDENTICAL to the contents of map_to_file()'s output, so I can upload it to a remote server via a mafia script & a small server script.

Edit: further analyzed, wouldn't work for what I'm doing, as some records have fields which are themselves aggregates.

Looking at his slyz's code (go slyz!) it's only a small change to make the string information identical to the file in terms of display. As for aggregates of records of aggregates...
That depends on how file_to_map works. I know it ignores information in excess of the map being retrieved, but does it fill defaults into fields that are omitted in the file?
That is, would file
Code:
0    Hello
Get stored to string[string, string] as ["Hello",""]=>"0"?
If so, then you'd only need to know the maximum depth from the starting aggregate to the final aggregate (counting the record itself as 1 field of depth).
 

StDoodle

Minion
As for aggregates of records of aggregates...

Yeah, that's actually what I meant; to avoid eleventy-billion different data files, I want a master record that contains an aggregate field for what would otherwise be its own data file. That's just how I roll.

That depends on how file_to_map works. I know it ignores information in excess of the map being retrieved, but does it fill defaults into fields that are omitted in the file?
That is, would file
Code:
0    Hello
Get stored to string[string, string] as ["Hello",""]=>"0"?
If so, then you'd only need to know the maximum depth from the starting aggregate to the final aggregate (counting the record itself as 1 field of depth).

How do you meaningfully iterate over sub-keys when you don't know the depth without generating an error? Or am I missing something?
 

slyz

Developer
Edit: further analyzed, wouldn't work for what I'm doing, as some records have fields which are themselves aggregates.
It doesn't really matter what your initial map contains, as long as you know how many fields you will have in the file that is written by map_to_file().

For aggregates of records which have an aggregate field, all you need to know is that map_to_file() adds the name of the record's fields as if they were keys themselves (the same way map_to_file( string, string, false) would do for an aggregate-less record):
this:
PHP:
record {
	int num;
	item itm;
	int [ item ] agr;
} [ int ] test_rec;

test_rec[ 0 ].num = 1;
test_rec[ 0 ].itm = $item[ big rock ];
test_rec[ 0 ].agr[ $item[ seal tooth ] ] = 99;
test_rec[ 0 ].agr[ $item[ hot butter roll ] ] = 99;

test_rec[ 1 ].num = 2;
test_rec[ 1 ].itm = $item[ disco banjo ];
test_rec[ 1 ].agr[ $item[ seal tooth ] ] = 999;
test_rec[ 1 ].agr[ $item[ hot butter roll ] ] = 999;

map_to_file( test_rec, "temp.txt" );
becomes:
Code:
0	num	1
0	itm	big rock
0	agr	hot buttered roll	99
0	agr	seal tooth	99
1	num	2
1	itm	Disco Banjo
1	agr	hot buttered roll	999
1	agr	seal tooth	999
and file_to_string( "temp.txt", 4 ) saves this string in the file2string Mafia property:
Code:
file2string=0\tagr\thot buttered roll\t99\n0\tagr\tseal tooth\t99\n0\titm\tbig rock\t\n0\tnum\t1\t\n1\tagr\thot buttered roll\t999\n1\tagr\tseal tooth\t999\n1\titm\tDisco Banjo\t\n1\tnum\t2

Looking at his slyz's code (go slyz!) it's only a small change to make the string information identical to the file in terms of display.
I know virtually nothing about text files ^^
What change would the string need? I only used \t for tabs and \n for end-of-lines, but that can be easily changed, especially if the string is meant to be passed to a site.

That depends on how file_to_map works. I know it ignores information in excess of the map being retrieved, but does it fill defaults into fields that are omitted in the file?
It does fill defaults, and the default string should be simply "". As you can see above, if your data file has a line with 4 fields and another line with 3 fields, the "" key will be added.

However, doing this:
PHP:
string [ int ] test;
test[ 0 ] = "hello";
map_to_file( test, "temp.txt" );
file_to_string( "temp.txt", 6 );
results in file2string being:
Code:
file2string=0\thello\tnone\tnone\tnone

I don't really know why the aggregate is filled with none instead of the default "" string, but I guess it has something to do with this:
Code:
> ash to_string( $item[none] );

Returned: none

I changed file_to_string() to account for this:
PHP:
string file_to_string( string file, int dim )
{
	if ( file == "" || dim < 2 )
	{
		print( "usage: print_map( string file, int dim ) - file cannot be empty, dim must be greater than 1", "red" );
		return "" ;
	}
	string command;
	command += "ashq " ;
	command += "string str; " ;
	command += "string [ " ;
	for i from 1 to (dim - 1)
	{
		command += "string" ;
		if( i < (dim - 1) ) command += ", " ;
	}
	command += " ] print_map; " ;
	command += "file_to_map( '" + file + "', print_map ); " ;
	command += "foreach " ;
	for i from 1 to dim
	{
		command += "str" + i ;
		if( i < dim ) command += ", " ;
	}
	command += " in print_map " ;
	command += " { " ;
	for i from 1 to dim
	{
		command += "if( str" + i + " == 'none' ) { str +='\\n'; continue; } " ;
		command += "else str += str" + i + " " ;
		if( i < dim ) command += "+ '\\t'; " ;
		else command += "+ '\\n'; " ;
	}
	command += " } " ;
	command += "set_property( 'file2string', str ); " ;
	
	
	cli_execute( command );
	//print( command );
	return get_property( 'file2string' );
}
I couldn't get replace_all() or replace_string() to work here, so I made this kludgy hack (writting a string pattern to work on a string that is loaded from an ash command written in a string... huh, how many backslashes do you need for the end command to be what you are expecting :confused:).

Now this:
PHP:
string [ int ] test;
test[ 0 ] = "hello";
map_to_file( test, "temp.txt" );
file_to_string( "temp.txt", 6 );
results in file2string being:
Code:
file2string=0\thello

I don't want to force StDoodle to use this, I just had fun with this, and now that I've taken it this far, it might as well actually work, right?
 
I know virtually nothing about text files ^^
What change would the string need? I only used \t for tabs and \n for end-of-lines, but that can be easily changed, especially if the string is meant to be passed to a site.

That would depend on where he's planning on posting the information.
It seems he'd be using it in the CLI, so... conversion to a table would probably be the easiest way to account for those tabs. Which isn't super hard, but not as trivial as I had originally conceived, either.

If he's POSTing the information, then I think visit_url takes care of the \n and \t for him, so he'd be set already for that.
 

StDoodle

Minion
I'd be POST'ing it via visit_url(), so I think that may work. Now I just need someone to work with me on the server-side (alas, don't have one I can play with) and I'll try fiddling around with it. Thanks everyone.
 
Top