function binarySearch(value)
{
var startIndex = 0,
stopIndex = words.length - 1,
middle = Math.floor((stopIndex + startIndex) / 2);
while (w
The best way is to use trees instead, since for an array such an operation is likely to have linear algorithmic complexity.
If you'd like to stick with the arrays, I suggest that you use the splice method for insertion.
For an efficient binary search insertion, you'll want to have your binary search return something that indicates where the string would belong in the array if it is not found.
The accepted method of doing this in other languages is to return the bitwise complement of the index where the string belongs. The bitwise complement of 0 is -1, the bitwise complement of 1 is -2, 2 is -3, and so on. To get the bitwise complement of a number in JavaScript, use the ~
operator.
Example code:
/*
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;
};
You can then use the binarySearch method to write your own binaryInsert function:
/*
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;
};
Once these methods are prototyped onto the array object, you can use them directly like so:
var arr = [];
arr.binaryInsert("Zebra");
arr.binaryInsert("Aardvark");
arr.binaryInsert("Mongoose");
alert(arr);
/* [ "Aardvark", "Mongoose", "Zebra" ] */
As the number of items increases, this will be signficantly faster than calling Array.sort()
Array Property Key Pollution
Note that prototyping methods onto the Array object as in the above code causes the methods to appear as enumerable properties of your arrays, which could interfere with any logic where you're enumerating all properties in a for(var i in arr)
loop. Loops written in the format of for(var i=0; i<arr.length; i++)
will still work as designed.
If you don't need to support Internet Explorer 8 or below, you can avoid calling Array.prototype
directly and instead use Object.defineProperty
as in the below examples.
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;
}
});
This approach will avoid polluting the enumerable keys, so for(var i in arr)
loops will still work as expected.