2つの配列に重複する要素を取り出す

MTQで、「2つの配列に重複する要素を、MTタグで取り出したい」という質問がありました。
例えば、1つ目の配列の要素が「赤、青、黄、緑、黒、茶」で、2つ目の配列の要素が「紫、白、青、橙、緑、赤」の時に、「赤、緑、青」を取り出すような場合です。
この質問にお答えします。

1.基本的な考え方

この例のように、「あるデータが、他のデータの並びに含まれているかどうか」を調べることは、プログラムの中ではよくあることです。
このような処理は、ハッシュを使った処理の中で、最も基本的なものです。

冒頭に挙げた例で、2つの配列の名前が、それぞれ「color1」「color2」であるとします。
この場合、まず配列color1の各要素から、「キーが色名、値が1」というハッシュを作ります。
ハッシュの内容は以下のようになります。

キー
1
1
1
1
1
1

次に、配列color2(紫、白、青、橙、緑、赤)から1つずつ要素を取り出します。
そして、上で作ったハッシュの中に、color2の各要素の値がキーになっている要素があるかどうかを調べます。
今取り上げている例だと、上のハッシュの中に、「キーが『紫』の要素がある」「キーが『白』の要素がある」・・・ということを順に調べ、要素があればその色を出力します。
これで、両方の配列に重複する要素を取り出すことができます。

2.テンプレート化する

ここまでの話をテンプレート化すると、以下のようになります。

<mt:Loop name="color1">
  <$mt:SetVar name="seen" key="$__value__" value="1">
</mt:Loop>
<mt:Loop name="color2">
  <mt:If name="seen" key="$__value__">
    <$mt:GetVar name="__value__"$>
  </mt:If>
</mt:Loop>

1~3行目は、配列color1からハッシュを作る処理です。
ハッシュには「seen」という名前を付けています。
MTLoopタグによって、配列の個々の要素が順に取り出され、変数__value__に代入されます。
ハッシュseenに、キーが__value__の値になっている要素を作り、その値を1にしています。

4~8行目は、配列color2の要素を順に取り出し、ハッシュseenと比較して、重複している色だけを出力する処理です。
MTLoopタグによって、変数__value__には、配列color2の個々の色が順に代入されます。
5行目のMTIfタグで、ハッシュseenの中で、キーが変数__value__の値(=color2の個々の色)になっている要素があるかどうかを調べます。これによって、配列color1にも存在する色かどうかが分かります。
5行目のMTIfタグの条件が成立すれば、その色は配列color1にも存在している(=配列color2と重複している)ので、6行目のMTGetVarタグでその色を出力します。

3.速度の問題

質問をされた方は、「MTLoop等を使うと、要素が多いと処理が重そう」ということも述べていました。
実際、2で作ったテンプレートは、要素の数にほぼ比例して処理時間が長くなります。
2と同様の処理をプラグイン化して、Perlで処理すれば、速度はだいぶ上がると思います。

ちなみに、2と同等の処理をPerlで書くと、以下のようになります。
配列color1とcolor2の両方に重複する要素を、配列color3に求めるようにしています。

my (%seen, @color3);
map { $seen{$_} = 1 } @color1;
@color3 = grep { $seen{$_} } @color2;