我最近發布了一個用於記錄 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,傑夫·帕金森 (Jeff Perkinson) 寫道:
關聯數組列表 = {
[ “鑰匙1” => “A” , “鑰匙2” => “b” ],
[ “鑰匙1” => “C” , “鑰匙2” => “d” ],
[ “鑰匙2” => “F” , “鑰匙1” => “e” , “鑰匙3” => “G” ],
};
您如何將其轉換為該數據表?
請注意以下幾點:
- 列根據數組的鍵命名
- 鍵以不同的順序出現
- 每個關聯數組中的鍵數量各不相同
我希望得到:
- JSL 閱讀起來相對容易理解。
- JSL 相對容易編寫,語句也最少。
- 無需大量循環即可提高效率。
@brady_brady有幾個解決方案。
其中一個是創造性的黑客,依靠高度結構化的問題,將鍵和值轉換為文本字符串,然後轉換為他操作的數據表。
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 = {
["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() 函數。 這是一個我不斷忘記但總是努力記住的技巧。 然而,他確實有最後一個循環和一些
表達式()使閱讀變得更加困難的功能。
此外,他承認假設密鑰的名稱一致是作弊的。 我會原諒這一點,因為這是一個可以通過他創建的數據結構的循環輕鬆克服的問題:
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>
- 一旦在關聯數組(行)之一中遇到缺失值(或缺少鍵),就會拋出此異常。
- 僅將字符串(字符)數據添加到表列中。源中的任何數字數據都將被忽略,從而導致值為數字的空列。
- 如果遇到完全數字數據,則列類型保留為字符,並且不會強制為數字。
我最終得到了以下結果,似乎可以解決這些問題:
aaListToDataTable = function ({aaList}, {Default Local},
unique=aaList[1];
for (i=1, i<=nitems(aaList), i++,
unique<<INSERT></INSERT>沒有在特別大的數據集上測試它的性能。
哇, @傑夫·珀金森和@菲爾_布朗,我來尋找一些有關通過關聯數組進行索引的一般內容,我得到了更多!
作為最近轉換為關聯數組(我過去有很多嵌套列表:\ )我正在嘗試更頻繁地使用它們,並且僅在這個線程中就必須至少有 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;