連想配列の基本操作【JavaScript/C#/VB/Python/CommonLisp/HSP】

最近いくつかの言語の連想配列の操作を調査したのでそれをここにまとめておく。

## JavaScript
JavaScriptで連想配列を使うには昔ながらのオブジェクトを使ったものとES6で追加されたMapクラスを使う方法があります。

### Object
```js
"use strict";

//初期化
const kantoh={
    "茨城県":"水戸市",
    "栃木県":"宇都宮市",
    "群馬県":"前橋市",
    "埼玉県":"さいたま市",
    "神奈川県":"横浜市"
};
//代入
kantoh["千葉県"]="千葉市";
//要素の有無
console.log("千葉県" in kantoh);
//追加と代入は同じ
kantoh["東京都"]="新宿区";
//要素が登録済みなら代入したくない場合
if(!("東京都" in kantoh)){
    kantoh["東京都"]="新宿区";
}
else{
    console.log("同一のキーを含む項目が既に追加されています。");
}
//取得
console.log(kantoh["千葉県"]);
//代入演算子も使用可
kantoh["千葉県"]+="中央区";
console.log(kantoh["千葉県"]);
//要素数
console.log(Object.keys(kantoh).length);
//要素の削除
delete kantoh["千葉県"];
//キーの配列
console.log(Object.keys(kantoh).join(","));
//値の配列
console.log(Object.values(kantoh).join(","));
//ループ
for(let [key,value] of Object.entries(kantoh)){
    console.log(`key:${key} value:${value}`);   
}
//Join
const joinDict=
    dict=>Object.entries(dict).map(v=>v.join(":")).join(", ");
console.log(joinDict(kantoh));
//要素のクリア
for(let i of Object.keys(kantoh)) delete kantoh[i];
kantoh["なし"]="なにもないよ";
console.log(joinDict(kantoh));
```

### Map
```js
"use strict";

//初期化
const kantoh=new Map([
	["茨城県","水戸市"],
	["栃木県","宇都宮市"],
	["群馬県","前橋市"],
	["埼玉県","さいたま市"],
	["神奈川県","横浜市"]
]);
//代入
kantoh.set("千葉県","千葉市");
//要素の有無
console.log(kantoh.has("千葉県"));
//追加と代入は同じ
kantoh.set("東京都","新宿区");
//要素が登録済みなら代入したくない場合
if(!kantoh.has("東京都")){
	kantoh.set("東京都","新宿区");
}
else{
	console.log("同一のキーを含む項目が既に追加されています。");
}
//取得
console.log(kantoh.get("千葉県"));
//代入演算子は使用不可
const resetAdd=(dict,key,value)=>dict.set(key,dict.get(key)+value);
resetAdd(kantoh,"千葉県","中央区");
console.log(kantoh.get("千葉県"));
//要素数
console.log(kantoh.size);
//要素の削除
kantoh.delete("千葉県");
//キーの配列
console.log(Array.from(kantoh.keys()));
//値の配列
console.log(Array.from(kantoh.values()));
//ループ
for(let [key,value] of kantoh){
	console.log(`key:${key} value:${value}`);   
}
//Join
const joinDict=
	dict=>Array.from(dict).map(v=>v.join(":")).join(", ");
console.log(joinDict(kantoh));
//要素のクリア
kantoh.clear();
kantoh.set("なし","なにもないよ");
console.log(joinDict(kantoh));
```

Mapは最近新しく追加された機能であるだけあってメソッドが綺麗に整備されています。
しかし、初期化や取得、代入リテラルがオブジェクトに比べて少し不自然なのが辛いところ。

# Python
```py
#初期化
kantoh={
    "茨城県":"水戸市",
    "栃木県":"宇都宮市",
    "群馬県":"前橋市",
    "埼玉県":"さいたま市",
    "神奈川県":"横浜市"
}
#代入
kantoh["千葉県"]="千葉市"
#要素の有無
print("千葉県" in kantoh)
#追加と代入は同じ
kantoh["東京都"]="新宿区"
#要素が登録済みなら代入したくない場合
if "東京都" not in kantoh:
    kantoh["東京都"]="新宿区"
else:
    print("同一のキーを含む項目が既に追加されています。")
#取得
print(kantoh["千葉県"])
#代入演算子も使用可
kantoh["千葉県"]+="中央区"
print(kantoh["千葉県"])
#要素数
print(len(kantoh))
#要素の削除
del kantoh["千葉県"]
#キーの配列
print(list(kantoh.keys()))
#値の配列
print(list(kantoh.values()))
#ループ
for key,value in kantoh.items():
    print(f"key:{key} value:{value}")
#Join
joinDict=(
	lambda dict:",".join([":".join(v) for v in dict.items()]))
print(joinDict(kantoh))
#要素のクリア
kantoh.clear()
kantoh["なし"]="なにもないよ"
print(joinDict(kantoh))
```
Pythonのdictは言語レベルで組み込まれていてリテラルも何も不自由なく使用できます。

### C<span>#</span>
```cs
using System;
using System.Collections.Generic;
using System.Linq;

class Program{
	static void Main(){
		//初期化
		var kantoh=new Dictionary<string,string>{
			{"茨城県","水戸市"},
			{"栃木県","宇都宮市"},
			{"群馬県","前橋市"},
			{"埼玉県","さいたま市"},
			{"神奈川県","横浜市"}
		};
		//代入
		kantoh["千葉県"]="千葉市";
		//要素の有無
		Console.WriteLine(kantoh.ContainsKey("千葉県"));
		//追加
		kantoh.Add("東京都","新宿区");
		//要素が登録済みの場合、ArgumentException
		try{
			kantoh.Add("東京都","新宿区");
		}
		catch(ArgumentException ex){
			Console.WriteLine(ex.Message);
		}
		//取得
		Console.WriteLine(kantoh["千葉県"]);
		//代入演算子も使用可
		kantoh["千葉県"]+="中央区";
		Console.WriteLine(kantoh["千葉県"]);
		//要素数
		Console.WriteLine(kantoh.Count);
		//要素の削除
		kantoh.Remove("千葉県");
		//キーの配列
		Console.WriteLine(string.Join(",",kantoh.Keys));
		//値の配列
		Console.WriteLine(string.Join(",",kantoh.Keys));
		//ループ
		foreach(var v in kantoh){
			Console.WriteLine($"key:{v.Key} value:{v.Value}");
		}
		//Join
		Func<Dictionary<string,string>,string> joinDict=
			dict=>string.Join(", ",dict.Select(v=>v.Key+":"+v.Value));
		Console.WriteLine(joinDict(kantoh));  
		//要素のクリア
		kantoh.Clear();
		kantoh["なし"]="なにもないよ";
		Console.WriteLine(joinDict(kantoh));  
	}
}
```
極々普通な辞書。静的言語なので型定義はしておく必要があります。

## VB
```vb
Option Strict On
Option Infer On
Imports System.Collections.Generic
Imports System.Linq

Module Program
	Sub Main()
		'初期化
		Dim kantoh As New Dictionary(Of String,String) From {
			{"茨城県","水戸市"},
			{"栃木県","宇都宮市"},
			{"群馬県","前橋市"},
			{"埼玉県","さいたま市"},
			{"神奈川県","横浜市"}
		}
		'代入
		kantoh("千葉県")="千葉市"
		'要素の有無
		Console.WriteLine(kantoh.ContainsKey("千葉県"))
		'追加
		kantoh.Add("東京都","新宿区")
		'要素が登録済みの場合、ArgumentException
		Try
			kantoh.Add("東京都","新宿区")
		Catch ex As ArgumentException
			Console.WriteLine(ex.Message)
		End Try
		'取得
		Console.WriteLine(kantoh("千葉県"))
		'代入演算子も使用可
		kantoh("千葉県")+="中央区"
		Console.WriteLine(kantoh("千葉県"))
		'要素数
		Console.WriteLine(kantoh.Count)
		'要素の削除
		kantoh.Remove("千葉県")
		'キーの配列
		Console.WriteLine(String.Join(",",kantoh.Keys))
		'値の配列
		Console.WriteLine(String.Join(",",kantoh.Keys))
		'ループ
		For Each v In kantoh
			Console.WriteLine($"key:{v.Key} value:{v.Value}")
		Next
		'Join
		Dim joinDict As Func(Of Dictionary(Of String,String),String)=
			Function(dict) String.Join(", ",dict.Select(Function(v) v.Key & ":" & v.Value))
		Console.WriteLine(joinDict(kantoh))
		'要素のクリア
		kantoh.Clear()
		kantoh("なし")="なにもないよ"
		Console.WriteLine(joinDict(kantoh))
	End Sub
End Module
```
C#と同じ。

# HSP
HSPの言語機能に連想配列なんてものは存在しないので
以前自作した連想配列モジュール[「DictionaryOnHSP」](https://github.com/yosgspec/DictionaryOnHSP)を使用します。
大体.NET(C#,VB)っぽい命令/関数群が用意されています。

```cpp
#runtime "hsp3cl"
#cmpopt varinit 1
#include "Dictionary.as"

;初期化
new@Dictionary kantoh,"str",,,{"
	茨城県:水戸市,
	栃木県:宇都宮市,
	群馬県:前橋市,
	埼玉県:さいたま市,
	神奈川県:横浜市
"}
;代入
dcSet kantoh,"千葉県","千葉市"
;要素の有無
mes dcContainsKey(kantoh,"千葉県")
;追加
dcAdd kantoh,"東京都","新宿区"
;要素が登録済みの場合、statに1が返り何もしない
dcAdd kantoh,"東京都","新宿区"
if 1=stat{
	mes "同一のキーを含む項目が既に追加されています。"
}
;取得
mes dcItem(kantoh,"千葉県")
;代入演算子っぽい何かも使用可
dcReSet kantoh,"千葉県","+","中央区"
mes dcItem(kantoh,"千葉県")
;要素数
mes dcCount(kantoh)
;要素の削除
dcRemove kantoh,"千葉県"
;キーの配列
sdim keys: dcRefKeys kantoh,keys
mes dcJoinArray(keys,",")
;値の配列
sdim values: dcRefValues kantoh,values
mes dcJoinArray(values,",")
;ループ
dcForeach kantoh
	mes strf("key:%s value:%s",dcKeys(kantoh,cnt),dcValues(kantoh,cnt))   
loop
;Join
mes dcJoinDict(kantoh,":",",")   
;要素のクリア
dcClear kantoh
dcSet kantoh,"なし","なにもないよ"
mes dcJoinDict(kantoh)
```

## CommonLisp
全体的にかなり冗長。
[連想リスト](https://lisphub.jp/common-lisp/cookbook/index.cgi?%E9%80%A3%E6%83%B3%E3%83%AA%E3%82%B9%E3%83%88%E3%82%92%E4%BD%BF%E3%81%86)という選択肢もあります。

```cl
(let(
		;初期化
		(kantoh (make-hash-table :test #'equal)))
	;初期化は愚直
	(setf
		(gethash "茨城県" kantoh) "水戸市"
		(gethash "栃木県" kantoh) "宇都宮市"
		(gethash "群馬県" kantoh) "前橋市"
		(gethash "埼玉県" kantoh) "さいたま市"
		(gethash "神奈川県" kantoh) "横浜市")
	(format t "~A~%" (gethash "千葉県" kantoh))
	;代入
	(setf (gethash "千葉県" kantoh) "千葉市")
	;要素の有無 gethashの返り値の2価を
	(format t "~A~%" (multiple-value-list(gethash "千葉県" kantoh)))
	;追加と代入は同じ
	(setf (gethash "東京都" kantoh) "新宿区")
	;要素が登録済みなら代入したくない場合
	(if (not(cadr(multiple-value-list(gethash "東京都" kantoh))))
		(setf (gethash "東京都" kantoh) "新宿区")
		(write-line "同一のキーを含む項目が既に追加されています。"))
	;取得
	(format t "~A~%" (gethash "東京都" kantoh))
	;代入演算子なんてない
	(setf (gethash "千葉県" kantoh)
		(concatenate 'string (gethash "千葉県" kantoh) "中央区"))
	;要素数
	(format t "~d~%" (hash-table-count kantoh))
	;要素の削除
	(remhash "千葉県" kantoh)
	;キーの配列
	(format t "~A~%" (loop for key being the hash-keys in kantoh collect key))
	;値の配列
	(format t "~A~%" (loop for key being the hash-keys in kantoh using (hash-value value) collect value))
	;ループ
	(maphash #'(lambda(key value)
			(format t "key:~A value:~A~%" key value))
		kantoh)
	;Join
	(labels((joinDict(dict)
			(format nil "~{~A~^,~}" 
				(mapcar #'(lambda(item) (concatenate 'string (car item) ":"  (cadr item)))
					(loop for key being the hash-keys in kantoh using (hash-value value) collect
						(list key value))))))
		(format t "~A~%" (joinDict kantoh))
		;要素のクリア
		(maphash #'(lambda(key value) (remhash key kantoh)) kantoh)
			(setf (gethash "なし" kantoh) "なにもないよ")
			(format t "~A~%" (joinDict kantoh))))
```