効率的なバイナリ検索の挿入のために、文字列が見つからない場合に文字列が配列のどこに属するかを示すものをバイナリ検索で返すようにする必要があります。
他の言語でこれを行うために受け入れられている方法は、文字列が属するインデックスのビット単位の補数を返すことです。0のビット単位の補数は-1、1のビット単位の補数は-2、2は-3などです。JavaScriptで数値のビット単位の補数を取得するには、~
演算子を使用します。
コード例:
/*
target: the object to search for in the array
comparator: (optional) a method for comparing the target object type
return value: index of a matching item in the array if one exists, otherwise the bitwise complement of the index where the item belongs
*/
Array.prototype.binarySearch = function (target, comparator) {
var l = 0,
h = this.length - 1,
m, comparison;
comparator = comparator || function (a, b) {
return (a < b ? -1 : (a > b ? 1 : 0)); /* default comparison method if one was not provided */
};
while (l <= h) {
m = (l + h) >>> 1; /* equivalent to Math.floor((l + h) / 2) but faster */
comparison = comparator(this[m], target);
if (comparison < 0) {
l = m + 1;
} else if (comparison > 0) {
h = m - 1;
} else {
return m;
}
}
return~l;
};
次に、binarySearchメソッドを使用して、独自のbinaryInsert関数を記述できます。
/*
target: the object to insert into the array
duplicate: (optional) whether to insert the object into the array even if a matching object already exists in the array (false by default)
comparator: (optional) a method for comparing the target object type
return value: the index where the object was inserted into the array, or the index of a matching object in the array if a match was found and the duplicate parameter was false
*/
Array.prototype.binaryInsert = function (target, duplicate, comparator) {
var i = this.binarySearch(target, comparator);
if (i >= 0) { /* if the binarySearch return value was zero or positive, a matching object was found */
if (!duplicate) {
return i;
}
} else { /* if the return value was negative, the bitwise complement of the return value is the correct index for this object */
i = ~i;
}
this.splice(i, 0, target);
return i;
};
これらのメソッドが配列オブジェクトにプロトタイプ化されると、次のように直接使用できます。
var arr = [];
arr.binaryInsert("Zebra");
arr.binaryInsert("Aardvark");
arr.binaryInsert("Mongoose");
alert(arr);
/* [ "Aardvark", "Mongoose", "Zebra" ] */
アイテムの数が増えると、これは電話をかけるよりも大幅に速くなりますArray.sort()
アレイプロパティキーの汚染
上記のコードのようにArrayオブジェクトにメソッドをプロトタイピングすると、メソッドが配列の列挙可能なプロパティとして表示され、for(var i in arr)
ループ内のすべてのプロパティを列挙しているロジックに干渉する可能性があることに注意してください。の形式で記述されたループは、for(var i=0; i<arr.length; i++)
引き続き設計どおりに機能します。
Internet Explorer 8以下をサポートする必要がない場合は、Array.prototype
直接呼び出すことを避け、代わりObject.defineProperty
に以下の例のように使用できます。
Object.defineProperty(Array.prototype, "binarySearch", {
value: function (target, comparator) {
var l = 0,
h = this.length - 1,
m, comparison;
comparator = comparator || function (a, b) {
return (a < b ? -1 : (a > b ? 1 : 0));
};
while (l <= h) {
m = (l + h) >>> 1;
comparison = comparator(this[m], target);
if (comparison < 0) {
l = m + 1;
} else if (comparison > 0) {
h = m - 1;
} else {
return m;
}
}
return~l;
}
});
Object.defineProperty(Array.prototype, "binaryInsert", {
value: function (target, duplicate, comparator) {
var i = this.binarySearch(target, comparator);
if (i >= 0) {
if (!duplicate) {
return i;
}
} else {
i = ~i;
}
this.splice(i, 0, target);
return i;
}
});
このアプローチにより、列挙可能なキーの汚染が回避されるため、for(var i in arr)
ループは引き続き期待どおりに機能します。