cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Browse apps to extend the software in the new JMP Marketplace
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?

So, do you know for certain that there is no way to test an associative array to see if a key exists without using the Get Keys function? Does an associative array always generate an error when you attempt to access a key that doesn't exist yet?

12 ms is going to be very problematic, that is a tremendously long time just to determine if a value exists at a certain key. I'm pretty certain other scripting languages do this in microsecond time scales; I know that Lua does it incredibly fast (compared to 12ms, at least). If I'm dealing with a data table of 300k rows, then actually that could be about 300k keys. So the execution time could increase to (300k/20k)*12 ms = 180 ms. BUT, that would then have to run about 300k times, so the total execution time becomes 15 hours (180 ms * 3e5 = 15 hours). Actually, it would probably be about half of that, since the associative array would be growing as the loop runs. But 7.5 hours is still too long.

However, perhaps it is not the Contains function which chews up the majority of the execution time (within the 12 ms) but the Get Keys function?  In my script, I wouldn't even have to run Get Keys at all if I just keep (in parallel) a separate List of keys that I had added into the associative array.

pmroz
Super User

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

You could wrap a simple test for the key value in a try() section.  Not sure of the performance vs contains.

found = 0;
try(
	b = aa["junk"];   // This key is not in the associative array
	found = 1;
);
show(found);   // Returns 1 if found, 0 if not found.  1 is the same as TRUE, 0 is the same as FALSE
tatarjj
Level I

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

Cool. I was hoping JMP had something like Try.  I will try Try :) and compare its execution time vs Contains when I get the time later.  Thank you very much for the help.  I will post updates when I have them.

tatarjj
Level I

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

I think I got it working finally.  I wish I could assign values to a list in the same line that I constructed it, and get rid of all the Insert Into commands.  Do I really have to do this:

	rowValue = {};
	Insert Into(rowValue, :Name("start_t"));
	Insert Into(rowValue, :Name("part_id"));
	Insert Into(rowValue, Row());
	

When I really just want to do this?

rowValue = {:Name("start_t"), :Name("part_id"), Row()};

With the latter, however, the value of the list at index 3 is literally just the text "Row()"!  Shouldn't it run the Row() function and return the value?!

 

 

Is there a known issue in Windows 10 with the Script window being incredibly sluggish?

 

Anyway, this is the script in the form where it finally APPEARS to be working (need to fully verify):

dt = Current Data Table();
dt << Clear Select;

lastTests = Associative Array();



//For(i = 1, i <= N Rows(dt), i++,

For Each Row (
	//Row() = i;
	
	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 = {};
	Insert Into(rowValue, :Name("start_t"));
	Insert Into(rowValue, :Name("part_id"));
	Insert Into(rowValue, Row());
	
	//rowValue = {:Name("start_t"), :Name("part_id"), i};
	
	hasValue = 0;
	try(
		junk = lastTests[partKey];
		hasValue = 1;
	);

	If ( hasValue ==  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;
//Show(N Items(listOfVals));

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

//Print(listOfVals[i][3]);
);

dt << Select Rows( listOfRowsToSelect );

 

 

pmroz
Super User

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

Still tricky.  Use evallist() to do it in one step.  Also, instead of saying if (hasvalue == 1, just say if (hasvalue,.  A positive integer is True in JSL, 0 is false.  Here are some ways to do what you want.  One way loops over all the rows explicitly, the other uses for each row.

dt = open("$sample_data\Big Class.jmp");
for (i = 1, i <= nrows(dt), i++,
	rowvalue = evallist({column(dt, "age")[i], column(dt, "height")[i], i});
	print(rowvalue);
);

current data table(dt);
for each row(
	rowvalue = evallist({as column(dt, "age"), as column(dt, "height"), row()});
// This works too
//	rowvalue = evallist({:name("age"), :name("height"), row()});
	print(rowvalue);
);