J'ai récemment posté un complément pour consigner chaque lancement de JMP .
Pendant que je le développais, j'ai pensé que j'étais intelligent en stockant les informations de journalisation sous forme de tableau associatif .
Voici un exemple de ce qui se trouve dans un seul fichier journal.
["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"]
Un tableau associatif permet au journal de s'auto-documenter au fur et à mesure que chaque élément de données est étiqueté. Cela rend également le journal extensible. Ainsi, à l'avenir, vous pourrez facilement ajouter plus d'informations aux journaux et tout programme existant l'adaptera ou l'ignorera.
Il est assez facile de lire un répertoire rempli de fichiers journaux dans une liste de tableaux associatifs comme celui-ci :
logfiles = Files In Directory( "./Logs" );
logs = {};
For( i = 1, i <= N Items( logfiles ), i++,
Insert Into( logs, Parse( Load Text File( "./Logs/" || logfiles[i] ) ) )
);
Cela donne une structure qui ressemble à ceci :
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"]
};
Ce à quoi je n'ai pas pensé, c'est qu'il n'est pas aussi facile de convertir cette liste de tableaux associatifs en une table de données avec des colonnes nommées d'après les clés et une ligne pour chaque élément de la liste. Cela peut être fait avec beaucoup de boucles sur la liste et les tableaux, mais j'espérais quelque chose d'un peu plus élégant.
J'ai donc fait quelque chose de similaire à ce que beaucoup d'entre vous font en postant une question dans le forum Discussions. J'ai envoyé un e-mail à notre équipe de développement et à d'autres experts de SAS pour leur demander comment ils procéderaient.
Le 6 juin 2017 à 18h16, Jeff Perkinson a écrit :
list_of_associative_arrays = {
[ "clé1" => "un" , "clé2" => "b" ],
[ "clé1" => "c" , "clé2" => "d" ],
[ "clé2" => "F" , "clé1" => "e" , "clé3" => "g" ],
} ;
Comment convertiriez-vous cela en ce tableau de données ?
Remarquez quelques choses :
- Les colonnes sont nommées en fonction des clés des tableaux
- Les touches apparaissent dans des ordres différents
- Le nombre de clés dans chaque tableau associatif varie
J'espérais obtenir :
- JSL qui était relativement facile à interpréter lors de sa lecture.
- JSL qui était relativement facile à écrire, avec le moins d'instructions.
- Efficacité (plutôt) sans beaucoup de bouclage.
Mais d'autres ont fourni des idées intéressantes qui valent la peine d'être explorées.
@brady_brady avait quelques solutions.
L'un était un hack créatif qui comptait sur un problème hautement structuré et convertissait les clés et les valeurs en chaînes de texte, puis en une table de données qu'il manipulait.
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>L'autre était, selon ses propres mots, sans intérêt et avait une paire de boucles imbriquées. Mais notez l'utilisation de scripts de table de données dans l'instruction finale. Vous le verrez à nouveau dans notre solution gagnante.
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>
C'est vraiment astucieux en ce sens qu'il tire parti de la boucle implicite dans le
Fonction J() . C'est une astuce que je continue d'oublier mais que j'essaie toujours de me rappeler. Cependant, il a cette boucle finale et un certain nombre de
Expr() fonctions qui rendent la lecture plus difficile.
En outre, il admet avoir triché en supposant que les clés sont nommées de manière cohérente. Je pardonnerai cela car c'est un problème qui est facilement surmonté avec une boucle sur la structure de données qu'il a créée :
colnames=colnm<<GET keys=""></GET>
Maintenant, @Craige_Hales ' solution:
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>
J'aime cela parce que la boucle finale est sur la table de données et utilise le script de table de données que nous avons vu pour la première fois dans
@brady_brady la solution. Vous pouvez être en désaccord, mais pour moi, c'est mieux que de boucler sur une structure de données JSL. C'est peut-être parce qu'il y a moins de caractères, ce qui facilite la lecture.
Mais, il utilise quelques maladroits
Alors que() boucles pour obtenir les clés et nommer les colonnes. Donc, je prendrai comme gagnant une combinaison de
@Craige_Hales ' et
@Joseph Morgan Les solutions 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 fonction J() - et sa boucle implicite - vous complique la lecture, @Joseph Morgan indique qu'elle pourrait être remplacée par une boucle For().
for( i=2,i<=nitems(list),i++, unique<<INSERT></INSERT>
Ce fut vraiment instructif pour moi d'étudier chaque solution et de voir ici un certain nombre d'astuces que je pourrai peut-être utiliser plus tard. Prenez un peu de temps et faites de même pour vous-même.
Comme dans tout langage, il existe de nombreuses façons d'accomplir la même chose. Chacun a ses avantages. L'astuce consiste à les connaître et à les appliquer là où elles sont les plus avantageuses.
Bonjour à tous. Je lisais ceci et cela semble plus compliqué que nécessaire. J'ai pu faire fonctionner la solution ci-dessous, une quantité de code assez minime et elle peut probablement être réduite un peu plus avec plus d'efforts.
//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 />
);Jacques
J'ai rencontré quelques problèmes avec l'approche recommandée dans le message d'origine, liés à cette boucle où les valeurs sont insérées dans le tableau pour chaque ligne :
<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>
- Cela se déclenche dès qu'une valeur manquante (ou l'absence d'une clé) est rencontrée dans l'un des tableaux associatifs (lignes).
- Ajoute uniquement des données de chaîne (caractère) aux colonnes du tableau.Toutes les données de la source qui sont numériques seront simplement ignorées, ce qui entraînera des colonnes vides où les valeurs sont numériques.
- Les types de colonne sont laissés sous forme de caractères et ne sont pas contraints en numérique si des données entièrement numériques sont rencontrées.
Je me suis retrouvé avec ce qui suit, qui semble résoudre ces problèmes:
aaListToDataTable = function ({aaList}, {Default Local},
unique=aaList[1];
for (i=1, i<=nitems(aaList), i++,
unique<<INSERT></INSERT>N'a pas testé cela pour les performances sur des ensembles de données particulièrement volumineux.
Ouah, @Jeff_Perkinson et @Phil_Brown , je suis venu chercher des informations générales sur l'indexation via des tableaux associatifs et j'en ai eu bien plus !
En tant que converti récent aux tableaux associatifs (tant de listes imbriquées dans mon passé
) J'essaie de les utiliser plus souvent et il doit y avoir au moins 4 idées distinctes dans ce fil seul que je peux fusionner dans différents ensembles de mon code !
Merci d'avoir posté ceci 
Très intéressant! Si je peux proposer encore une autre approche, similaire à celle de Brady mais légèrement différente.
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;