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
miguello
Level VI

Arbitrary Operations (functions etc.) on each item in a list

Is there a more simple way of applying the same function on each item of a list rather than iterating through a list?

For example:

stringlist = {"1", "2"};
numericlist = {};
for (i = 1, i<=N Items(stringlist), i++,
	numericlist[i] = Num(stringlist[i])
	
);
show(numericlist);

Is there something simpler, something like just saying:

numericlist = Num(stringlist);

but this obviously doesn't work. Any other way?

P.S. Just found For Each function new in JMP 16, unfortunately, I'm still on JMP 15

2 ACCEPTED SOLUTIONS

Accepted Solutions
jthi
Super User

Re: Arbitrary Operations (functions etc.) on each item in a list

As you have already noticed JMP16 will help with this one with For Each and Transform Each. If you have to do this many times, maybe you could create custom functions to perform the tasks?

 

Names Default To Here(1);

charToNum = function({tempList}, {Default Local},
	newList = {};
	For(i = 1, i <= N Items(tempList), i++,
		newList[i] = Num(tempList[i]);
	);
	return(newList);
);

a = {"1", "2"};
b = charToNum(a);
show(b);

 

Edit:

You can also create more complicated function which could handle different transformations. Most likely will be overkill and I wouldn't really recommend it (I would just create different functions for different transformations).

 

Here is one example how it could be done:

Names Default To Here(1);

transformList = function({tempList, transformExpression}, {Default Local},
	newList = {};
	For(i = 1, i <= N Items(tempList), i++,
		val = tempList[i];
		newList[i] = transformExpression
	);
	return(newList);
);

a = {"1", "2", "100"};
b = transformList(a, Expr(Num(val)));
c = transformList(b, Expr(Multiply(val, 10)));
d = transformList(c, Expr(Char(val)));
Show(a,b,c,d);


//can also use custom functions with transformList
padZero = function({tempValue}, {Default Local},
	if(Length(tempValue) < 2,
		tempValue = "0" || tempValue;
	);
	return(tempValue)
);
e = transformList(a, Expr(padZero(val)));
Show(e);
f = transformList(e, Expr(Length(val)));
Show(f);

 

-Jarmo

View solution in original post

gzmorgan0
Super User (Alumni)

Re: Arbitrary Operations (functions etc.) on each item in a list

@miguello ,

 

@jthi gave you excellent answers (Kudos).  I too was so excited for JMP 16's function TransformEach. 

 

Below is a modified portion of a script written for much earlier versions of JMP and published for JMP13. Many of my former colleagues used the LFUNC function (name is abbreviation for list function).  The JMP 16 name "TransformEach"  and syntax is so good. However, if any one of the list items is the incorrect type for the specified transformation just an error is returned...a Try() function can fix that.

 

So Just In Case this might be of some use until you get to JMP 16, I am posting it.  Just save the function definition portion of code in a file and use Include() in any script you need it.

 

// JSL Companion: Applications of the JMP Scripting Language 
// Title: 	JSLC_CustomListFunctions.jsl 
// Version: Windows JMP® 13.2.0
// Purpose: Provides examples of writing user functions. 

/*Notes:
    These functions are provided for learning as well as utility.
    See Chapter 5 and 5_Extra_CustomListFunctions.jsl");
    
    The script will define 2 functions. After each is a comment
    block /*: Usage & Test examples ...  --- end Test*/. Remove
    The comment block and run the examples one line at a time. Open
    the embedded log or have the log window visible to see 
    the results.
  
  Motivation:  
    As seen in Chapter 5, a numeric function on a single layer
    list of numbers, or a vector, or a matrix, will return the
    same object with the function applied to each item. For example,
    xx = Sqrt({1,2,3,4,9}); //xx = {1, 1.4142135623731, 1.73205080756888, 2, 3}
     
    We have suggested that the same apply to String functions (that operate
    on a  single item) on a list  of text.  What we would like is
    xx = Substr({"Quaker", "Minnesota", "Nebraska"}, 1,1); would return
    xx = {"Q", "M", "N"}. However, currently most JMP String functions do not 
    allow list agruments.
  
  Functions defined here, are:
    - LFunc( mylist, expr); where expression is like expr(Substr(x,1,1)).
    
{LIKE function notes removed}    
    LFunc can save writing for-loops, Like save looking up the REGEX syntax.
*/

      
//------LFunc-------------------------------------------------------------------   
Lfunc = Function( {xList, fExpr/*ex. Expr(Contains(x,"h"))*/},
	{x = {}, i = 1, errCode = 0, errStr = "\!NError bad argument", newExpr},
	If( !Is List( xList ),
		errCode = 1
	);
	If(
		Type( Name Expr( fExpr ) ) == "Name", 
 //if Name then JMP does not recognize the function
			tmp = Trim( Char( Name Expr( fExpr ) ) );
  //extract the function and check if it is contained in the global namespace, if not error!
			If( Namespace( "global" ) << Contains( Word( 1, tmp, ":(" ) ) == 0,
				errCode = errCode + 2
			);,
		Type( Name Expr( fExpr ) ) != "Expression", errCode = errCode + 2
	); 
 //catches typos, returns Name if not a valid function
	//catches typos, returns Name if not a valid function
	If( !Is Expr( Name Expr( fExpr ) ),
		errCode = errCode + 2
	);   //catches syntax
	If(
		!errCode,
			For( i = 1, i <= N Items( xList ), i++,
				newExpr = Substitute( Name Expr( fExpr ), Expr( x ), xList[i] );
				Insert Into( x, Try( newExpr, Empty() ) );
			),
		errCode == 1, Write( errStr || "(1) not a list \!N" ),
		errCode == 2, Write( errStr || "(2) not a valid expression \!N" ),
		errCode == 3, Write( errStr || "s (1) is not a list and (2) is not a valid expression \!N" )
	);
	x//return x
	;
); //End Lfunc
//------------------------------------------------------------------------------   
 
//Usage & Test ...remove the slash asterisk and run in segments, results are in the log
//------------------------------------------------------------------------------    
  myList1={"john", "harry", "huh","ah ha", "xyx", , "a", "h", 7, [1,7]};
  
  /* example below returns a list of numeric values, one value for each item in the list 
    n =>1st location of h, 0 => does not contain "h", Empty() => the item is not 
    a valid argument for that expression.
  */

 zz1 = Lfunc(myList1,Expr(Contains(x,"h")));  
 show(zz1);

myList2={"john", "harry", "huh","ah ha", "xyx", , "a", "h"};
zz2=Lfunc(myList2, Expr(Uppercase(x)));
show(zz2);
//-----------------------------------------------------------------------------------------
/* returns a list of how many words are in each string 
   [] -> no occurrences
   Empty() => the expression is not valid for that item.
*/
zz3 = Lfunc(mylist2, expr(nitems(words(x))));
show(zz3);
//-----------------------------------------------------------------------------------------
/* This specified function requires numeric arguments 
   Empty() => the expression is not valid for that item.
   A numeric function applied to a matrix returns a matrix 
   of the results applied on each element in the matrix.
   Only the last 2 items are valid arguments for Log()
*/

zz4 = Lfunc(mylist1, expr(log(x)));
show(zz4);
//----------------Error Checking---------------------------------------------------------------
/* Uncomment the code below to show the error capture and reporting */

//zz = Lfunc("oh johhny", Expr(Contains(x,"h")) );     //not a list

//zz = Lfunc({"oh johhny"}, Expr(Contians(x,"h")) );  //type not a valid expr due to typo

//zz =  Lfunc("oh johhny", Expr(Contians(x,"h")) );   //not a list and not a valid expr

 

 

 

View solution in original post

4 REPLIES 4
jthi
Super User

Re: Arbitrary Operations (functions etc.) on each item in a list

As you have already noticed JMP16 will help with this one with For Each and Transform Each. If you have to do this many times, maybe you could create custom functions to perform the tasks?

 

Names Default To Here(1);

charToNum = function({tempList}, {Default Local},
	newList = {};
	For(i = 1, i <= N Items(tempList), i++,
		newList[i] = Num(tempList[i]);
	);
	return(newList);
);

a = {"1", "2"};
b = charToNum(a);
show(b);

 

Edit:

You can also create more complicated function which could handle different transformations. Most likely will be overkill and I wouldn't really recommend it (I would just create different functions for different transformations).

 

Here is one example how it could be done:

Names Default To Here(1);

transformList = function({tempList, transformExpression}, {Default Local},
	newList = {};
	For(i = 1, i <= N Items(tempList), i++,
		val = tempList[i];
		newList[i] = transformExpression
	);
	return(newList);
);

a = {"1", "2", "100"};
b = transformList(a, Expr(Num(val)));
c = transformList(b, Expr(Multiply(val, 10)));
d = transformList(c, Expr(Char(val)));
Show(a,b,c,d);


//can also use custom functions with transformList
padZero = function({tempValue}, {Default Local},
	if(Length(tempValue) < 2,
		tempValue = "0" || tempValue;
	);
	return(tempValue)
);
e = transformList(a, Expr(padZero(val)));
Show(e);
f = transformList(e, Expr(Length(val)));
Show(f);

 

-Jarmo
gzmorgan0
Super User (Alumni)

Re: Arbitrary Operations (functions etc.) on each item in a list

@miguello ,

 

@jthi gave you excellent answers (Kudos).  I too was so excited for JMP 16's function TransformEach. 

 

Below is a modified portion of a script written for much earlier versions of JMP and published for JMP13. Many of my former colleagues used the LFUNC function (name is abbreviation for list function).  The JMP 16 name "TransformEach"  and syntax is so good. However, if any one of the list items is the incorrect type for the specified transformation just an error is returned...a Try() function can fix that.

 

So Just In Case this might be of some use until you get to JMP 16, I am posting it.  Just save the function definition portion of code in a file and use Include() in any script you need it.

 

// JSL Companion: Applications of the JMP Scripting Language 
// Title: 	JSLC_CustomListFunctions.jsl 
// Version: Windows JMP® 13.2.0
// Purpose: Provides examples of writing user functions. 

/*Notes:
    These functions are provided for learning as well as utility.
    See Chapter 5 and 5_Extra_CustomListFunctions.jsl");
    
    The script will define 2 functions. After each is a comment
    block /*: Usage & Test examples ...  --- end Test*/. Remove
    The comment block and run the examples one line at a time. Open
    the embedded log or have the log window visible to see 
    the results.
  
  Motivation:  
    As seen in Chapter 5, a numeric function on a single layer
    list of numbers, or a vector, or a matrix, will return the
    same object with the function applied to each item. For example,
    xx = Sqrt({1,2,3,4,9}); //xx = {1, 1.4142135623731, 1.73205080756888, 2, 3}
     
    We have suggested that the same apply to String functions (that operate
    on a  single item) on a list  of text.  What we would like is
    xx = Substr({"Quaker", "Minnesota", "Nebraska"}, 1,1); would return
    xx = {"Q", "M", "N"}. However, currently most JMP String functions do not 
    allow list agruments.
  
  Functions defined here, are:
    - LFunc( mylist, expr); where expression is like expr(Substr(x,1,1)).
    
{LIKE function notes removed}    
    LFunc can save writing for-loops, Like save looking up the REGEX syntax.
*/

      
//------LFunc-------------------------------------------------------------------   
Lfunc = Function( {xList, fExpr/*ex. Expr(Contains(x,"h"))*/},
	{x = {}, i = 1, errCode = 0, errStr = "\!NError bad argument", newExpr},
	If( !Is List( xList ),
		errCode = 1
	);
	If(
		Type( Name Expr( fExpr ) ) == "Name", 
 //if Name then JMP does not recognize the function
			tmp = Trim( Char( Name Expr( fExpr ) ) );
  //extract the function and check if it is contained in the global namespace, if not error!
			If( Namespace( "global" ) << Contains( Word( 1, tmp, ":(" ) ) == 0,
				errCode = errCode + 2
			);,
		Type( Name Expr( fExpr ) ) != "Expression", errCode = errCode + 2
	); 
 //catches typos, returns Name if not a valid function
	//catches typos, returns Name if not a valid function
	If( !Is Expr( Name Expr( fExpr ) ),
		errCode = errCode + 2
	);   //catches syntax
	If(
		!errCode,
			For( i = 1, i <= N Items( xList ), i++,
				newExpr = Substitute( Name Expr( fExpr ), Expr( x ), xList[i] );
				Insert Into( x, Try( newExpr, Empty() ) );
			),
		errCode == 1, Write( errStr || "(1) not a list \!N" ),
		errCode == 2, Write( errStr || "(2) not a valid expression \!N" ),
		errCode == 3, Write( errStr || "s (1) is not a list and (2) is not a valid expression \!N" )
	);
	x//return x
	;
); //End Lfunc
//------------------------------------------------------------------------------   
 
//Usage & Test ...remove the slash asterisk and run in segments, results are in the log
//------------------------------------------------------------------------------    
  myList1={"john", "harry", "huh","ah ha", "xyx", , "a", "h", 7, [1,7]};
  
  /* example below returns a list of numeric values, one value for each item in the list 
    n =>1st location of h, 0 => does not contain "h", Empty() => the item is not 
    a valid argument for that expression.
  */

 zz1 = Lfunc(myList1,Expr(Contains(x,"h")));  
 show(zz1);

myList2={"john", "harry", "huh","ah ha", "xyx", , "a", "h"};
zz2=Lfunc(myList2, Expr(Uppercase(x)));
show(zz2);
//-----------------------------------------------------------------------------------------
/* returns a list of how many words are in each string 
   [] -> no occurrences
   Empty() => the expression is not valid for that item.
*/
zz3 = Lfunc(mylist2, expr(nitems(words(x))));
show(zz3);
//-----------------------------------------------------------------------------------------
/* This specified function requires numeric arguments 
   Empty() => the expression is not valid for that item.
   A numeric function applied to a matrix returns a matrix 
   of the results applied on each element in the matrix.
   Only the last 2 items are valid arguments for Log()
*/

zz4 = Lfunc(mylist1, expr(log(x)));
show(zz4);
//----------------Error Checking---------------------------------------------------------------
/* Uncomment the code below to show the error capture and reporting */

//zz = Lfunc("oh johhny", Expr(Contains(x,"h")) );     //not a list

//zz = Lfunc({"oh johhny"}, Expr(Contians(x,"h")) );  //type not a valid expr due to typo

//zz =  Lfunc("oh johhny", Expr(Contians(x,"h")) );   //not a list and not a valid expr

 

 

 

miguello
Level VI

Re: Arbitrary Operations (functions etc.) on each item in a list

Thanks, guys!

 

This is awesome.

I actually found JMP16 in our intranet, IT Department said they were just in the process of preparing installation instructions and releasing it officially. So I went ahead and installed it anyways.

My actual task(s) was(were) a little bit more complicated, as an example, in one instance I had a list of strings of type

"120, 130, 160, 120, 300, 200"

Actual data would be a column with each row having that type of string (only much much longer)

I needed to turn that into list of lists of numbers. So I needed to read that column into an initial list, and then apply Words() on this initial list to get list of lists of strings and then Num() to get each string into a number. This means two nested loops. 

 

For now I've done that with two For Each loops, but let me look into suggestions that you posted here. I never could comprehend Expr() - I guess I need to give it another chance.

 

Thanks!

jthi
Super User

Re: Arbitrary Operations (functions etc.) on each item in a list

If your case is like that, then you could possibly avoid looping at least for the transformation by using Parse() (though it might be faster to loop through the string).

 

Names Default To Here(1);
str = "120, 130, 160, 120, 300, 200"; numList = Parse("{" || str ||"}"); Show(numList[1], numList[2]);

As data table:

Names Default To Here(1);
dt = New Table("Untitled",
	Add Rows(12),
	Compress File When Saved(1),
	New Column("Column 1",
		Character,
		"Nominal",
		Set Values(
			{"120, 130, 160, 120, 300, 200", "120, 130, 160, 120, 300, 200",
			"120, 130, 160, 120, 300, 200", "120, 130, 160, 120, 300, 200",
			"120, 130, 160, 120, 300, 200", "120, 130, 160, 120, 300, 200",
			"120, 130, 160, 120, 300, 200", "120, 130, 160, 120, 300, 200",
			"120, 130, 160, 120, 300, 200", "120, 130, 160, 120, 300, 200",
			"120, 130, 160, 120, 300, 200", "120, 130, 160, 120, 300, 200"}
		)
	)
);

dt << New Column("numlist", Expression, << set each value(Parse("{" || :Column 1|| "}")));
numListList = dt:numList << get as matrix;
Show(numListList, numListList[1], numListList[1][1]);
-Jarmo