cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Check out the JMP® Marketplace featured Capability Explorer add-in
Choose Language Hide Translation Bar
tatarjj
Level I

Is it possible to iterate through all elements in an associative array?

I would like to use a loop to iterate through all values in an associative array.   For the life of me, I cannot find the interator function for associative arrays. Is it simply impossible to step through all values in an associative array if they are not at pre-known keys?!

 

In my associative array, the keys are strings and the values are lists.  I need to iterate through the entire associative array, and for each key, read something from the value. 


This is my (incomplete) script.  I am a semi-veteran of some programming languages but I am a noob at JMP script.

 

Where I put comments inside /*  */ is where there is code that I don't know how to do.  I am stuck at /*For all parts in lastTests*/ near the bottom, though I don't know if the rest of my script works because I haven't tried running any of it yet.

 

By the way, the JMP scripting guide has a very brief section on associative arrays and it does not tell you how to iterate through elements within an associative array :(

 

Please help!

dt = Current Data Table();


lastTests = Associative Array();


For Each Row (
 
 partKey = Char(:Name("214:ReadOTP:OTP_Row_40[1]") + 256*:Name("215:ReadOTP:OTP_Row_41[1]") + 256*256*:Name("223:ReadOTP:OTP_Row_49[1]") + 256*256*256*:Name("224:ReadOTP:OTP_Row_4a[1]") + 256*256*256*256*:Name("123:LOG_SETPOINT_SPEA:SPEA_Setpoint[1]"));
 
 rowValue = {:Name("start_t"), :Name("part_id"), Row()};
 
 If (Is List(lastTests[partKey]) == 1,
 If ( rowValue[1] > lastTests[partKey][1], //if start_t is later
 lastTests[partKey] = rowValue,
 
 //else if
 (rowValue[1] == lastTests[partKey][1]) & (rowValue[2] > lastTests[partKey][2]), //if start_t is the same but part_id is larger
 lastTests[partKey] = rowValue;
 
 
 ),
 //else
 lastTests[partKey] = rowValue;
 
 );
 
 
);

listOfRowsToSelect = {};

/*For all parts in lastTests*/(
 selectedPart = /*get present item*/;
 Insert Into(listOfRowsToSelect, selectedPart[3]);

);

dt << Select Rows( listOfRowsToSelect );

/*Invert Selection*/;

/*Hide and Exclude selected rows*/;

dt << Clear Select;

 

 

14 REPLIES 14
tatarjj
Level I

Re: Is it possible to iterate through all elements in an associative array?

As an example, this is how you iterate through all values in a table (named "t") in Lua, a common scripting language:

for key, val in pairs (t) do

<stuff>

end

 

What is the equivalent in JMP scripting language for associative arrays?

 

txnelson
Super User

Re: Is it possible to iterate through all elements in an associative array?

To find the functions and messages available to an Associative Array, go to

     Help==>Scripting Index==>Associative Array

There you will find several ways of looping through the keys, or values by retrieving them into a list and then using a For() loop, etc.  Below is an example taken directly from the examples given in the Scripting Index using "First" and "Next" to loop through an Associative Array

Names Default To Here( 1 );
Local(
	{aa = [1 => "bun",
	2 => "shoe",
	3 => "tree",
	4 => "door"], x, words = ""},
	x = aa << First;
	While( !Is Empty( x ),
		words = words || aa[x];
		x = aa << Next( x );
	);
	words;
);

Also, starting on Page 207 in the JSL Scripting Guide there are several pages of documentation on how to Create and Use Associative Arrays in JMP

     Help==>Books==>Scripting Guide

Jim
tatarjj
Level I

Re: Is it possible to iterate through all elements in an associative array?

Thank you!  I did not know about the Help->Scripting Index!

It looks like in my case, I need to use the Get Values function (I don't need the keys).

Re: Is it possible to iterate through all elements in an associative array?

Be aware that there is no ordering of the contents of the associative array. Indexing is out. That is the reason for the First and Next messages to iterate over the array. Using the Get Values message returns an ordered result, but the contents are not necessarily in this order.

tatarjj
Level I

Re: Is it possible to iterate through all elements in an associative array?

Thanks Mark, IMO, this is how it should operate and it's the same as how similar data structures in other languages operate.

 

How does one test if an associative array has a value assigned to specific key? Since I am assigning specific "Char" (the same as string?) key values to lists, I first tried the Is List function, but it gave me an error. This was my script:

 

 

dt = Current Data Table();


lastTests = Associative Array();


For Each Row (
	
	partKey = Char(:Name("214:ReadOTP:OTP_Row_40[1]") + 256*:Name("215:ReadOTP:OTP_Row_41[1]") + 256*256*:Name("223:ReadOTP:OTP_Row_49[1]") + 256*256*256*:Name("224:ReadOTP:OTP_Row_4a[1]") + 256*256*256*256*Abs(:Name("123:LOG_SETPOINT_SPEA:SPEA_Setpoint[1]")));
	
	rowValue = {:Name("start_t"), :Name("part_id"), Row()};
	
	If (Is List(lastTests[partKey]) ==  1,
		If ( rowValue[1] > lastTests[partKey][1], //if start_t is later
			lastTests[partKey] = rowValue,
		
		//else if
		(rowValue[1] == lastTests[partKey][1]) & (rowValue[2] > lastTests[partKey][2]), //if start_t is the same but part_id is larger
			lastTests[partKey] = rowValue;
			
		
		),
	//else
		lastTests[partKey] = rowValue;
	
	);
	
	
);

listOfRowsToSelect = {};

listOfVals = lastTests << Get Values;

For(i = 1, i <= nrows(listOfVals), i++,
	Insert Into(listOfRowsToSelect, listOfVals[i][3]);

);

dt << Select Rows( listOfRowsToSelect );

And this is the error I see in the log:

 

subscripted key not in map["192344950106"]{1} in access or evaluation of 'partKey' , partKey/*###*/

In the following script, error marked by /*###*/
dt = Current Data Table();
lastTests = Associative Array();
For Each Row(
	partKey = Char(
		:Name( "214:ReadOTP:OTP_Row_40[1]" ) + 256 *
		:Name( "215:ReadOTP:OTP_Row_41[1]" ) + 256 * 256 *
		:Name( "223:ReadOTP:OTP_Row_49[1]" ) + 256 * 256 * 256 *
		:Name( "224:ReadOTP:OTP_Row_4a[1]" ) + 256 * 256 * 256 * 256 *
		Abs( :Name( "123:LOG_SETPOINT_SPEA:SPEA_Setpoint[1]" ) )
	);
	rowValue = {:start_t, :part_id, Row()};
	If( Is List( lastTests[partKey/*###*/] ) == 1,
		If(
			rowValue[1] > lastTests[partKey][1], lastTests[partKey] = rowValue,
			rowValue[1] == lastTests[partKey][1] & rowValue[2] > lastTests[partKey][
			2], lastTests[partKey] = rowValue
		),
		lastTests[partKey] = rowValue
	);
);
listOfRowsToSelect = {};
listOfVals = lastTests << Get Values;
For( i = 1, i <= N Rows( listOfVals ), i++,
	Insert Into( listOfRowsToSelect, listOfVals[i][3] )
);
dt << Select Rows( listOfRowsToSelect );

 

tatarjj
Level I

Re: Is it possible to iterate through all elements in an associative array?

I also tried the Is Empty function (switching the 1 to a 0 in the comparison as well), and it gave me the same error.

pmroz
Super User

Re: Is it possible to iterate through all elements in an associative array?

The getkeys method returns all of the keys for an associative array.

aa = associative array();
aa["One"] = {1, 2, 3};
aa["Two"] = {"a", "b", "c"};
key_list = aa << getkeys();

// Results shown in the log:
{"One", "Two"}
tatarjj
Level I

Re: Is it possible to iterate through all elements in an associative array?

Yes, but this script and its derivatives I plan to make need to operate on a data table with well over three hundred thousand rows, each with hundreds of columns (if my computer can't handle that; if not, then I'll have to break it up some, but the table size for each database fragment will still be HUGE). 

 

So, it is critically important to optimize this code so that it runs as efficiently as possible.  Running Get Keys on the associative array and then seaching the huge list of tens or hundreds of thousands of entries simply to see if the returned list contains a value seems HORRIBLY inefficient when all I need to do is see if lastTests[partKey] has a value assigned to it or not!

pmroz
Super User

Re: Is it possible to iterate through all elements in an associative array?

Understood.  I did a test with an associative array with 20,000 keys.  I used a contains search to look for each of the 20,000 keys; took 12 seconds.  Looking for an individual key takes a fraction of a second.

// Look for one key
start = hp time();
zlist = aa << getkeys();	// Contains 20,000 keys
a     = "xyz9999";
flag  = contains(zlist, a);
end   = hp time();
show((end - start)/1000000);
// .012 seconds

// Search for all keys using CONTAINS
start = hp time();
for (i = 1, i <= nitems(xlist), i++,   // xlist contains the keys in order of creation
	x = xlist[i];
	b = contains(zlist, x);        // zlist comes from getkeys and is not in order of creation
);
end = hp time();
show((end - start)/1000000);
// 12.2 seconds