配列の隙間を詰める関数を作ったよ

こんなことに使えるよ

JavaScriptで配列の隙間を詰める関数を作ってみました。例えば、こんな風に間にnull要素が混じる配列があったとすると。

[9,6,null,5,7,null,8]

間を詰めて、こんな風にしてくれます。

[9,6,5,7,8,null,null]

今回これを実現するためにsqueezeという関数を作りました。使い方は以下のとおりです。

var array = [9,6,null,5,7,null,8]; //対象の配列を
squeeze(array); //squeeze関数に渡します

基準点を変更することもできるよ

例えば、先ほどの例は配列の前に詰めていましたが、後ろに詰めることも可能です。こんな配列があると…。

[9,6,null,5,7,null,8]

一番最後を基準にして詰めてくれます。

[null,null,9,6,5,7,8]

squeezeの使い方はこんな風になります。

var array = [9,6,null,5,7,null,8];
squeeze(array, array.length - 1); //第二引数を指定

squeezeの第二引数は、どこに向かって詰めるのか、基準となる点です。今回後ろに詰めるという話だったので、配列の一番最後の位置を指定しました。基準点を中央に設定することもできます。こんな配列があったとき。

[9,6,null,5,7,null,8]

7の要素が入っている部分、つまり配列の添え字4番を基準点として指定します。すると、この部分を基準にして配列が詰められます。

[null,9,6,5,7,8,null]

これを実現するには、以下のようなコードになります。

var array = [9,6,null,5,7,null,8];
squeeze(array, 4);

詰める対象を変更できるよ

先ほどはnullの部分を詰めていましたが、詰める対象を変更することもできます。今度は0の部分を空とみなして、詰めましょう。

[9,6,0,5,7,0,8]

上の配列を

[9,6,5,7,8,0,0]

こんな風にします。

これを実現するには、以下のようなコードになります。

var array = [9,6,null,5,7,null,8];
squeeze(array, null, 0);

第二引数は、nullを指定しています。第二引数を省略するか、nullにするか、0を指定することで、前詰めになります。そして、第三引数は空の要素とみなす値を代入します。今回はたまたま数値ですが、真偽値やオブジェクトでも大丈夫です。

これを実現するsqueeze関数のソースコードは以下のようになります。

異常が発生するとfalseが返ってきます。

/**
 * 配列の要素に空のものが混じっていれば、隙間を詰める
 * @param {Object} array 対象となる配列
 * @param {Object} centerIndex(省略可能) どこを基準に配列を詰めるのか.省略すると最初の要素を基準にする.
 * @param {Object} emptyElement(省略可能) 何を空の要素とみなすか
 */
function squeeze(array, centerIndex, emptyElement){
    if (!Object.isArray(array)) 
        return false;
    if (centerIndex == null || !Object.isNumber(centerIndex)) 
        centerIndex = 0;
    if (emptyElement == undefined) 
        emptyElement = null;
    
    /*
     * 配列の隙間を詰めるアルゴリズム
     * 参考 http://www.geocities.jp/oldbig_ancient/KodaiHP3.htm
     *
     * 基準点を中心として、正の方向と負の方向のチェックをする
     */
    //正の方向に対するチェック
    var pointer = centerIndex; //配列要素の移動先
    for (var i = centerIndex; i < array.length; i++) {
        var currentCell = array[i];
        if (i != pointer && currentCell != emptyElement) {
            array[pointer] = currentCell;
            array[i] = emptyElement;
        }
        if (array[pointer] != emptyElement) { //空っぽじゃなくなったら次に進む
            pointer++;
        }
    }
    //負の方向に対するチェック
    var pointer = centerIndex; //配列要素の移動先
    for (var i = centerIndex; 0 <= i; i--) {
        var currentCell = array[i];
        if (i != pointer && currentCell != emptyElement) {
            array[pointer] = currentCell;
            array[i] = emptyElement;
        }
        if (array[pointer] != emptyElement) { //空っぽじゃなくなったら次に進む
            pointer--;
        }
    }
    return true;
}