ある状態を配列で持つことにして、ある状態と別のある状態を比較し、消えた要素と現れた要素を知りたい(ただしどのインデックスの要素が消えたりしたのかは気にしない)という状況を想定。各配列は重複する要素を持たないものとする。
Ruby の配列は集合のように扱うことができることを活用すると、単純にこんな感じで書ける。
def array_diff(older, newer) # 引数はどっちも配列 removed = older - newer # 配列 older から配列 newer に存在する要素を除去 added = newer - older # 配列 newer から配列 older に存在する要素を除去 return removed, added # 消えた要素からなる配列と現れた要素からなる配列を返す end p array_diff(["a", "b", "c", "d", "e", "f"], ["a", "bb", "c", "e", "g"]) # => [["b", "d", "f"], ["bb", "g"]]
つまりは差集合の演算。Ruby には集合を扱う Set クラスもあって同様の演算ができるようなのだが、今回は順序付きである必要があったので。
ところで、同様の関数を JavaScript で作ろうとするともうちょっと複雑になる。今回は JavaScript 1.6 で追加された Array の新メソッドを 2 つ(indexOf()
と filter()
)使って配列の差分をとり、さらに JavaScript 1.7 で追加された分割代入 (destructuring assignment) とその使用例として複数の値を返す関数を使う。
// JavaScript 1.7 function arrayDiff(aOlder, aNewer){ function f(aElement, aIndex, aArray){ // コールバック関数 /* filter の第 1 引数の各要素についてこの関数は繰り返し実行される。 filter の第 2 引数がこの関数内の this になる。 */ return (this.indexOf(aElement) == -1); // 配列 this にその要素が含まれなければ true } /* filter メソッドは対象の配列の各要素について反復し、 各要素について第 1 引数のコールバック関数(今回は f)が実行され、 その関数が true を返した要素からなる新たな配列を返す。 */ var removed = aOlder.filter(f, aNewer); // aOlder の要素のうち aNewer に含まれないものからなる配列 var added = aNewer.filter(f, aOlder); // aNewer の要素のうち aOlder に含まれないものからなる配列 return [removed, added]; // 複数の値を返す } var [removed, added] = arrayDiff(["a", "b", "c", "d", "e", "f"], ["a", "bb", "c", "e", "g"]); // 分割代入
こんな感じで書ける。