最近投稿しましたJMP の各起動をログに記録するアドイン。
開発中、ログ情報を連想配列。
単一のログ ファイルの内容の例を次に示します。
["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"]
連想配列により、各データにラベルが付けられるため、ログが自己文書化されます。 また、ログを拡張可能になります。 したがって、将来的には、ログにさらに多くの情報を簡単に追加できるようになり、既存のプログラムはそれを適応または無視するようになります。
次のように、ログ ファイルでいっぱいのディレクトリを連想配列のリストに読み取るのは非常に簡単です。
logfiles = Files In Directory( "./Logs" );
logs = {};
For( i = 1, i <= N Items( logfiles ), i++,
Insert Into( logs, Parse( Load Text File( "./Logs/" || logfiles[i] ) ) )
);
これにより、次のような構造が得られます。
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"]
};
ただし、私が考えなかったのは、連想配列のリストを、キーにちなんで名付けられた列とリスト内の各項目の行を備えたデータ テーブルに変換するのがそれほど簡単ではないということです。 リストと配列を何度もループすることで実現できますが、私はもう少しエレガントなものを期待していました。
そこで私は、ディスカッション フォーラムに質問を投稿することで、多くの人が行うことと同様のことを行いました。 私は開発チームとSASの他の専門家に電子メールを送り、どのようにするかを尋ねました。
2017年6月6日午後6時16分、ジェフ・パーキンソンは次のように書きました。
連想配列のリスト = {
[ 「キー1」 => 「あ」 、 「キー2」 => 「b」 ]、
[ 「キー1」 => 「c」 、 「キー2」 => 「d」 ]、
[ 「キー2」 => 「ふ」 、 「キー1」 => 「え」 、 「キー3」 => 「ぐ」 ]、
};
これをこのデータテーブルにどのように変換しますか?
いくつかの点に注意してください。
- 列の名前は配列のキーに基づいて付けられます。
- キーは異なる順序で表示されます
- 各連想配列のキーの数は異なります
私は次のことを期待していました:
- 読んだときに比較的解釈しやすかったJSL。
- ステートメントが最も少なく、比較的簡単に作成できる JSL。
- ループをあまり行わずに効率を高めます。
しかし、検討する価値のある興味深いアイデアを提供した人もいます。
@brady_bradyいくつかの解決策がありました。
1 つは、高度に構造化された問題を利用し、キーと値をテキスト文字列に変換し、その後操作するデータ テーブルに変換する創造的なハッキングでした。
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>もう 1 つは、彼自身の言葉によれば、刺激的ではなく、入れ子になったループが 2 つありました。 ただし、最後のステートメントでデータ テーブル スクリプトが使用されていることに注意してください。 私たちの優れたソリューションでもそれがわかります。
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>
これは、暗黙的なループを利用するという点で非常に賢いものです。
J()関数。 これは私が忘れがちなコツですが、常に覚えておくようにしています。 ただし、彼には最後のループといくつかのループがあります。
Expr()読みにくくする機能。
また、キーの名前が一貫していると仮定して不正行為を行ったことも認めています。 これは、彼が作成したデータ構造をループすることで簡単に克服できる問題なので、これは許します。
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>
私がこれを気に入っているのは、最後のループがデータ テーブルを対象としており、最初に見たデータ テーブル スクリプトを使用しているからです。
@brady_bradyさんの解決策。 異論はあるかもしれませんが、私にとっては、JSL データ構造をループするよりもその方が良いと思います。 文字数が少なくて読みやすいからかもしれません。
// 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>
J() 関数とその暗黙的なループによって読みにくくなる場合は、 @ジョセフ・モーガンFor() ループに置き換えることができることを指摘しています。
for( i=2,i<=nitems(list),i++, unique<<INSERT></INSERT>
これは、各ソリューションを研究し、後で使用できる可能性のあるいくつかのトリックをここで確認するのに非常に有益でした。 時間をかけて自分自身にも同じことをしてください。
他の言語と同様、同じことを実現する方法はたくさんあります。 それぞれに利点があります。 重要なのは、それらを知り、最も有利な部分に適用することです。
やあ。 私はこれを読んでいて、必要以上に複雑に感じました。 私は、かなり最小限のコード量で、以下のソリューションを機能させることができました。おそらく、もう少し努力すれば、コードをさらに減らすことができます。
//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 />
);ジェイク
元の投稿で推奨されているアプローチでは、行ごとに値がテーブルに挿入されるこのループに関連して、いくつかの問題が発生しました。
<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>
- これは、連想配列 (行) の 1 つで欠損値 (またはキーの不在) が見つかるとすぐにスローされます。
- 文字列 (文字) データのみをテーブル列に追加します。ソース内の数値データはすべて無視され、値が数値である空の列が作成されます。
- 列タイプは文字のままであり、完全に数値データが見つかった場合でも数値に強制されません。
最終的には次のようになりました。これはこれらの問題に対処しているようです。
aaListToDataTable = function ({aaList}, {Default Local},
unique=aaList[1];
for (i=1, i<=nitems(aaList), i++,
unique<<INSERT></INSERT>特に大規模なデータセットでのパフォーマンスについてはテストしませんでした。
おお、 @ジェフ・パーキンソンと@Phil_Brown , 連想配列によるインデックス作成に関する一般的な情報を探しに来ましたが、他にもたくさんの情報がありました。
最近、連想配列に変換しました (過去に非常に多くのネストされたリストがありました:\ ) 私はそれらをより頻繁に使用しようとしていますが、このスレッドだけでも少なくとも 4 つの別個のアイデアがあるはずです。コードのさまざまなセットにマージできます。
投稿してくれてありがとう:)
また、要素がいくつあるかを知る必要がないという事実も気に入っています。 While( IsEmpty(...) ) を使用すると、配列の長さを動的にすることができます。
とても興味深い! ブレイディと似ていますが、少し異なる、さらに別のアプローチを提案できます。
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;