Hace poco publiqué un complemento para registrar cada lanzamiento de JMP .
Mientras lo desarrollaba, pensé que estaba siendo inteligente al almacenar la información de registro como un matriz asociativa .
Este es un ejemplo de lo que hay en un solo archivo de registro.
["Bitness" => "64-bit",
"Build Information" => "Jan 9 2017, 00:53:08, Release, JMP ",
"Date" => "06/06/2017 08:05:03 AM",
"Host" => "Windows",
"Hostname" => "computer1",
"JMP Version" => "13.1.0",
"Product Name" => "Standard",
"Username" => "joeuser"]
Una matriz asociativa hace que el registro se autodocumente a medida que se etiqueta cada dato. También hace que el registro sea extensible. Por lo tanto, en el futuro puede agregar fácilmente más información a los registros y cualquier programa existente la adaptará o la ignorará.
Es bastante fácil leer un directorio lleno de archivos de registro en una lista de matrices asociativas como esta:
logfiles = Files In Directory( "./Logs" );
logs = {};
For( i = 1, i <= N Items( logfiles ), i++,
Insert Into( logs, Parse( Load Text File( "./Logs/" || logfiles[i] ) ) )
);
Esto da como resultado una estructura que se ve así:
logs = {
["Bitness" => "64-bit",
"Build Information" => "Jan 9 2017, 00:53:08, Release, JMP ",
"Date" => "06/06/2017 02:53:43 PM",
"Host" => "Windows",
"Hostname" => "MFIDT0259",
"JMP Version" => "13.1.0",
"Product Name" => "Standard",
"Username" => "pol"],
["Bitness" => "64-bit",
"Build Information" => "Jan 9 2017, 00:53:08, Release, JMP ",
"Date" => "06/06/2017 08:30:03 AM",
"Host" => "Windows",
"Hostname" => "MFIDT0259",
"JMP Version" => "13.1.0",
"Product Name" => "Standard",
"Username" => "pol"]
};
Sin embargo, lo que no pensé es que no es tan fácil convertir esa lista de matrices asociativas en una tabla de datos con columnas que llevan el nombre de las claves y una fila para cada elemento de la lista. Se puede hacer con muchos bucles sobre la lista y las matrices, pero esperaba algo un poco más elegante.
Entonces, hice algo similar a lo que muchos de ustedes hacen al publicar una pregunta en el foro de Discusiones. Envié un correo electrónico a nuestro equipo de desarrollo y a otros expertos de SAS preguntando cómo lo harían.
El 6 de junio de 2017 a las 18:16, Jeff Perkinson escribió:
list_of_asociative_arrays = {
[ "clave1" => "a" , "clave2" => "b" ],
[ "clave1" => "C" , "clave2" => "d" ],
[ "clave2" => "F" , "clave1" => "mi" , "clave3" => "gramo" ],
};
¿Cómo convertirías esto en esta tabla de datos?
Note algunas cosas:
- Las columnas se nombran en función de las claves de las matrices.
- Las teclas aparecen en diferentes órdenes.
- El número de claves en cada matriz asociativa varía
esperaba conseguir:
- JSL que fue relativamente fácil de interpretar al leerlo.
- JSL que fue relativamente fácil de escribir, con la menor cantidad de declaraciones.
- Eficiencia (ish) sin muchos bucles.
Pero algunos otros proporcionaron ideas interesantes que vale la pena explorar.
@brady_brady tenía un par de soluciones.
Uno fue un truco creativo que contó con un problema altamente estructurado y convirtió las claves y los valores en cadenas de texto y luego en una tabla de datos que manipuló.
l = {
["key1" => "c", "key2" => "d", "key3" => "g2", "key4" => "h"],
["key1" => "a", "key2" => "e", "key3" => "g3", "key4" => "o"],
["key1" => "b", "key2" => "f", "key3" => "g1", "key4" => "i"],
["key1" => "c", "key2" => "a", "key3" => "g6", "key4" => "k"]
};
nt = New Table( "x", new column("Column 1", character), new column("Group", character) );
n_l = nitems(l);
n_k = nitems(l[1]<<GET keys="">,]")[2::2*n_l*n_k::2];
refs = words(char(l), "{} [\!"=>,]")[1::2*n_l*n_k-1::2];
column(nt,1)<<SET values=""></SET></GET>El otro era, según sus propias palabras, aburrido y tenía un par de bucles anidados. Pero observe el uso de secuencias de comandos de tablas de datos en la declaración final. Lo verá nuevamente en nuestra solución ganadora.
l = {
["key1" => "a", "key2" => "b"],
["key2" => "f", "key1" => "e", "key3" => "g"],
["key1" => "c", "key2" => "d", "key4" => "h"],
["key2" => "f2", "key1" => "e2", "key3" => "g2"]
};
nt = New Table( "challenge" );
colList = {};
For( i = 1, i <= N Items( l ), i++,
nt << addrows( 1 );
ki = l[i] << get keys;
For( j = 1, j <= N Items( ki ), j++,
If( !Contains( colList, ki[j] ),
nt << New Column( ki[j], character );
Insert Into( colList, ki[j] );
);
Column( nt, ki[j] )[i] = l[i][ki[j]];
);
);
nt << delete column( 1 );
l = {
["key1" => "a", "key2" => "b"],
["key1" =>"c", "key2" => "d"],
["key2" => "f", "key1" => "e", "key3" => "g"]
};
// get unique column names – alas, cannot just count items in associative arrays
colnm=l[1]; i=1;
j(nitems(l),1,colnm<<INSERT></INSERT>
Esto es realmente inteligente porque aprovecha el bucle implícito en el
Función J() . Este es un truco que sigo olvidando pero siempre trato de recordar. Sin embargo, tiene ese bucle final y una serie de
Expr() funciones que dificultan la lectura.
Además, admite haber hecho trampa al asumir que las claves se nombran de manera consistente. Lo perdonaré, ya que es un problema que se supera fácilmente con un bucle sobre la estructura de datos que ha creado:
colnames=colnm<<GET keys=""></GET>
Ahora, @Craige_Hales ' solución:
list = {
["key9" => "k9r1", "key2" => "k2r1"],
["key2" => "k2r2", "key1" => "k1r2", "key3" => "k3r2"],
["key1" => "k1r3", "key2" => "k2r3", "key4" => "k4r3"],
["key2" => "k2r4", "key1" => "k1r4", "key7" => "k7r4"]
};
nonunique = list<<GETKEYS></GETKEYS>
Me gusta esto porque el ciclo final está sobre la tabla de datos y usa la secuencia de comandos de la tabla de datos que vimos por primera vez en
@brady_brady la solución de . Puede que no esté de acuerdo, pero para mí eso se siente mejor que recorrer una estructura de datos JSL. Tal vez sea porque tiene menos caracteres y eso hace que sea más fácil de leer.
Pero, él usa un par de torpes
Mientras() bucles para obtener las claves y nombrar las columnas. Entonces, tomaré como ganador una combinación de
@Craige_Hales ' y
@Joseph Morgan soluciones de:
// Revised
list = {
["key9" => "k9r1", "key2" => "k2r1"],
["key2" => "k2r2", "key1" => "k1r2", "key3" => "k3r2"],
["key1" => "k1r3", "key2" => "k2r3", "key4" => "k4r3"],
["key2" => "k2r4", "key1" => "k1r4", "key7" => "k7r4"]
};
unique=list[1];
i=1;
j(nitems(list),1,unique<<INSERT></INSERT>
Si la función J(), y su bucle implícito, le dificultan la lectura, @Joseph Morgan señala que podría reemplazarse con un bucle For().
for( i=2,i<=nitems(list),i++, unique<<INSERT></INSERT>
Esto fue realmente instructivo para mí para estudiar cada solución y ver una serie de trucos aquí que puedo usar más adelante. Tómate un tiempo y haz lo mismo por ti.
Como cualquier idioma, hay muchas maneras de lograr lo mismo. Cada uno tiene sus ventajas. El truco está en conocerlos y aplicarlos donde sean más ventajosos.
Hola. Estaba leyendo esto y se siente más complicado de lo que debe ser. Pude hacer que la solución a continuación funcionara, una cantidad mínima de código y probablemente pueda reducirse un poco más con más esfuerzo.
//perc_array is my associative array. it has two levels if you want to call them that. the first is a text string, the second is numeric;<BR /><BR />perc_array= [1 => "Sum(Qty 1kHz)", 2 => "Sum(Qty 1KHZ_LT50)", 3 => "Sum(Qty 50Hz_High)", 4 => "Sum(Qty 50Hz_Low)", 5 => "Sum(Qty DC)", 6 => "Sum(Qty ERROR)", 7 => "Sum(Qty GA1GA2)", 8 => "Sum(Qty Idd)", 9 => "Sum(Qty Idd_Low)", 10 => "Sum(Qty THD)", "Sum(Qty 1kHz)" => 0.00202316156420758, "Sum(Qty 1KHZ_LT50)" => 0.000757081600136874, "Sum(Qty 50Hz_High)" => 0.0176374349048836, "Sum(Qty 50Hz_Low)" => 0.00692494412780564, "Sum(Qty DC)" => 0.00482906851160752, "Sum(Qty ERROR)" => 0.00296416694290878, "Sum(Qty GA1GA2)" => 0.124725985649668, "Sum(Qty Idd)" => 0.00232257236652159, "Sum(Qty Idd_Low)" => 0.000327213233957462, "Sum(Qty THD)" => 0.000032079728819359];<BR /><BR />//the denominator below is how many "columns" of keys you have or how many levels;
ncheck=n items(perc_Array())/2;<BR /><BR />//xx is the variable I use to know how many "rows" i have in the associative array;
dt_sort=new table("SortFT", Add Rows( xx-1 ),
New Column( "Label", Character),
New Column( "%", Numeric, Format("Percent",2));
);
for(i=1, i<=ncheck, i++,
dt_sort:Label[i]=perc_array[i];
dt_sort:Name("%")[i]=perc_array[dt_sort:Label[i]];<BR />
);jake
Me encontré con algunos problemas con el enfoque recomendado en la publicación original, relacionados con este ciclo donde los valores se insertan en la tabla para cada fila:
<SPAN class="token function">foreachrow</SPAN><SPAN class="token punctuation">(</SPAN>
i <SPAN class="token operator">=</SPAN> <SPAN class="token function">row</SPAN><SPAN class="token punctuation">(</SPAN><SPAN class="token punctuation">)</SPAN><SPAN class="token punctuation">;</SPAN>
dt<SPAN class="token punctuation">[</SPAN>i<SPAN class="token punctuation">,</SPAN>list<SPAN class="token punctuation">[</SPAN>i<SPAN class="token punctuation">]</SPAN><SPAN class="token operator"><</SPAN><SPAN class="token operator"><</SPAN><SPAN class="token messages">getkeys] </SPAN><SPAN class="token operator">=</SPAN> list<SPAN class="token punctuation">[</SPAN>i<SPAN class="token punctuation">]</SPAN><SPAN class="token operator"><</SPAN><SPAN class="token operator"><</SPAN><SPAN class="token messages">getvalues</SPAN><SPAN class="token punctuation">;</SPAN>
<SPAN class="token punctuation">)</SPAN><SPAN class="token punctuation">;</SPAN>
- Esto se lanza tan pronto como se encuentra un valor faltante (o la ausencia de una clave) en una de las matrices asociativas (filas).
- Solo agrega datos de cadena (caracteres) a las columnas de la tabla.Cualquier dato en la fuente que sea numérico simplemente se ignorará, lo que dará como resultado columnas vacías donde los valores son numéricos.
- Los tipos de columna se dejan como caracteres y no se fuerzan a ser numéricos si se encuentran datos enteramente numéricos.
Terminé con lo siguiente, que parece abordar estos problemas:
aaListToDataTable = function ({aaList}, {Default Local},
unique=aaList[1];
for (i=1, i<=nitems(aaList), i++,
unique<<INSERT></INSERT>No probé esto para el rendimiento en conjuntos de datos particularmente grandes.
Guau, @Jeff_Perkinson y @Phil_Brown , ¡Vine en busca de algunas cosas generales sobre la indexación a través de matrices asociativas y obtuve mucho más!
Como una conversión reciente a matrices asociativas (tantas listas anidadas en mi pasado: \ ) ¡Estoy tratando de usarlos con más frecuencia y debe haber al menos 4 ideas separadas en este hilo solo que puedo fusionar en varios conjuntos de mi código!
Gracias por publicar esto 
¡Muy interesante! Si se me permite ofrecer otro enfoque, similar al de Brady pero ligeramente diferente.
list = {
["key9" => "k9r1", "key2" => "k2r1"],
["key2" => "k2r2", "key1" => "k1r2", "key3" => "k3r2"],
["key1" => "k1r3", "key2" => "k2r3", "key4" => "k4r3"],
["key2" => "k2r4", "key1" => "k1r4", "key7" => "k7r4"]
};
newdt = New Table();
cols ={};
For( i = 1, i <= N Items( list ), i++,
newdt << addRows( 1 );
cKey = list[i] << first;
While( !Is Empty( cKey ),
If( !Contains(cols, cKey),
newdt << newColumn( cKey, Character );
InsertInto( cols, cKey);
);
Column( cKey )[i] = list[i][ckey];
cKey = list[i] << next( cKey );
);
);
newdt << deleteColumns( 1 );
newdt << reOrderByName;