prototype/src/lang/array.js

303 lines
8.3 KiB
JavaScript
Raw Normal View History

2009-03-05 19:56:01 +00:00
/** section: Language, alias of: Array.from
2008-12-14 04:36:59 +00:00
* $A(iterable) -> Array
*
* Accepts an array-like collection (anything with numeric indices) and returns
* its equivalent as an actual Array object.
* This method is a convenience alias of [[Array.from]], but is the preferred way
* of casting to an Array.
**/
function $A(iterable) {
2007-01-18 22:24:27 +00:00
if (!iterable) return [];
if (iterable.toArray) return iterable.toArray();
var length = iterable.length || 0, results = new Array(length);
while (length--) results[length] = iterable[length];
return results;
2007-01-18 22:24:27 +00:00
}
if (Prototype.Browser.WebKit) {
$A = function(iterable) {
if (!iterable) return [];
// In Safari, only use the `toArray` method if it's not a NodeList.
// A NodeList is a function, has an function `item` property, and a numeric
// `length` property. Adapted from Google Doctype.
if (!(typeof iterable === 'function' && typeof iterable.length ===
'number' && typeof iterable.item === 'function') && iterable.toArray)
return iterable.toArray();
var length = iterable.length || 0, results = new Array(length);
while (length--) results[length] = iterable[length];
return results;
};
}
2009-03-05 19:56:01 +00:00
/** section: Language
2008-12-14 04:36:59 +00:00
* $w(string) -> Array
* - string (String): A string with zero or more spaces.
*
* Splits a string into an array, treating all whitespace as delimiters.
*
* Equivalent to Ruby's `%w{foo bar}` or Perl's `qw(foo bar)`.
**/
2008-12-11 10:31:36 +00:00
function $w(string) {
if (!Object.isString(string)) return [];
string = string.strip();
return string ? string.split(/\s+/) : [];
}
2007-01-18 22:24:27 +00:00
2008-12-11 10:31:36 +00:00
Array.from = $A;
2007-01-18 22:24:27 +00:00
2009-03-05 19:56:01 +00:00
/** section: Language
* class Array
2008-12-14 04:36:59 +00:00
**/
2008-12-11 10:31:36 +00:00
(function() {
var arrayProto = Array.prototype,
slice = arrayProto.slice,
_each = arrayProto.forEach; // use native browser JS 1.6 implementation if available
function each(iterator) {
2007-01-18 22:24:27 +00:00
for (var i = 0, length = this.length; i < length; i++)
iterator(this[i]);
2008-12-11 10:31:36 +00:00
}
if (!_each) _each = each;
2007-01-18 22:24:27 +00:00
2008-12-14 04:36:59 +00:00
/**
* Array#clear() -> Array
* Empties an array.
**/
2008-12-11 10:31:36 +00:00
function clear() {
2007-01-18 22:24:27 +00:00
this.length = 0;
return this;
2008-12-11 10:31:36 +00:00
}
2008-12-14 04:36:59 +00:00
/**
* Array#first() -> ?
* Returns the array's first item.
**/
2008-12-11 10:31:36 +00:00
function first() {
2007-01-18 22:24:27 +00:00
return this[0];
2008-12-11 10:31:36 +00:00
}
2008-12-14 04:36:59 +00:00
/**
* Array#last() -> ?
* Returns the array's last item.
**/
2008-12-11 10:31:36 +00:00
function last() {
2007-01-18 22:24:27 +00:00
return this[this.length - 1];
2008-12-11 10:31:36 +00:00
}
2008-12-14 04:36:59 +00:00
/**
* Array#compact() -> Array
* Trims the array of `null`, `undefined`, or other "falsy" values.
**/
2008-12-11 10:31:36 +00:00
function compact() {
2007-01-18 22:24:27 +00:00
return this.select(function(value) {
return value != null;
});
2008-12-11 10:31:36 +00:00
}
2008-12-14 04:36:59 +00:00
/**
* Array#flatten() -> Array
* Returns a flat (one-dimensional) version of the array.
*
* Nested arrays are recursively injected inline. This can prove very
* useful when handling the results of a recursive collection algorithm,
* for instance.
**/
2008-12-11 10:31:36 +00:00
function flatten() {
2007-01-18 22:24:27 +00:00
return this.inject([], function(array, value) {
2008-12-11 10:31:36 +00:00
if (Object.isArray(value))
return array.concat(value.flatten());
array.push(value);
return array;
2007-01-18 22:24:27 +00:00
});
2008-12-11 10:31:36 +00:00
}
2008-12-14 04:36:59 +00:00
/**
* Array#without(value...) -> Array
* - value (?): A value to exclude.
*
* Produces a new version of the array that does not contain any of the
* specified values.
**/
2008-12-11 10:31:36 +00:00
function without() {
var values = slice.call(arguments, 0);
2007-01-18 22:24:27 +00:00
return this.select(function(value) {
return !values.include(value);
});
2008-12-11 10:31:36 +00:00
}
2008-12-14 04:36:59 +00:00
/**
* Array#reverse([inline = false]) -> Array
* - inline (Boolean): Whether to modify the array in place. If `false`,
* clones the original array first.
*
* Returns the reversed version of the array.
**/
2008-12-11 10:31:36 +00:00
function reverse(inline) {
2007-01-18 22:24:27 +00:00
return (inline !== false ? this : this.toArray())._reverse();
2008-12-11 10:31:36 +00:00
}
2008-12-14 04:36:59 +00:00
/**
* Array#reduce() -> Array
* Reduces arrays: one-element arrays are turned into their unique item,
* while multiple-element arrays are returned untouched.
**/
2008-12-11 10:31:36 +00:00
function reduce() {
2007-01-18 22:24:27 +00:00
return this.length > 1 ? this : this[0];
2008-12-11 10:31:36 +00:00
}
2008-12-14 04:36:59 +00:00
/**
* Array#uniq([sorted = false]) -> Array
* - sorted (Boolean): Whether the array has already been sorted. If `true`,
* a less-costly algorithm will be used.
*
* Produces a duplicate-free version of an array. If no duplicates are
* found, the original array is returned.
**/
2008-12-11 10:31:36 +00:00
function uniq(sorted) {
return this.inject([], function(array, value, index) {
if (0 == index || (sorted ? array.last() != value : !array.include(value)))
array.push(value);
return array;
2007-01-18 22:24:27 +00:00
});
2008-12-11 10:31:36 +00:00
}
2008-12-14 04:36:59 +00:00
/**
* Array#intersect(array) -> Array
* - array (Array): A collection of values.
*
* Returns an array containing every item that is shared between the two
* given arrays.
**/
2008-12-11 10:31:36 +00:00
function intersect(array) {
return this.uniq().findAll(function(item) {
return array.detect(function(value) { return item === value });
});
2008-12-11 10:31:36 +00:00
}
2008-12-14 04:36:59 +00:00
/** alias of: Array#toArray
* Array#clone() -> Array
*
* Returns a duplicate of the array, leaving the original array intact.
**/
2008-12-11 10:31:36 +00:00
function clone() {
return slice.call(this, 0);
}
2008-12-14 04:36:59 +00:00
/** related to: Enumerable#size
* Array#size() -> Number
* Returns the size of the array.
*
* This is just a local optimization of the mixed-in [[Enumerable#size]]
* which avoids array cloning and uses the arrays native length property.
**/
2008-12-11 10:31:36 +00:00
function size() {
2007-01-18 22:24:27 +00:00
return this.length;
2008-12-11 10:31:36 +00:00
}
2008-12-14 04:36:59 +00:00
/** related to: Object.inspect
* Array#inspect() -> String
*
* Returns the debug-oriented string representation of an array.
**/
2008-12-11 10:31:36 +00:00
function inspect() {
2007-01-18 22:24:27 +00:00
return '[' + this.map(Object.inspect).join(', ') + ']';
2008-12-11 10:31:36 +00:00
}
2008-12-14 04:36:59 +00:00
/** related to: Object.toJSON
* Array#toJSON() -> String
*
* Returns a JSON string representation of the array.
**/
2008-12-11 10:31:36 +00:00
function toJSON() {
var results = [];
this.each(function(object) {
var value = Object.toJSON(object);
if (!Object.isUndefined(value)) results.push(value);
});
return '[' + results.join(', ') + ']';
2007-01-18 22:24:27 +00:00
}
2008-12-11 10:31:36 +00:00
2008-12-14 04:36:59 +00:00
/**
* Array#indexOf(item[, offset = 0]) -> Number
* - item (?): A value that may or may not be in the array.
* - offset (Number): The number of initial items to skip before beginning the
* search.
*
* Returns the position of the first occurrence of `item` within the array or
* `-1` if `item` doesnt exist in the array.
**/
2008-12-11 10:31:36 +00:00
function indexOf(item, i) {
i || (i = 0);
var length = this.length;
if (i < 0) i = length + i;
for (; i < length; i++)
if (this[i] === item) return i;
return -1;
}
2008-12-14 04:36:59 +00:00
/** related to: Array#indexOf
* Array#lastIndexOf(item[, offset]) -> Number
* - item (?): A value that may or may not be in the array.
* - offset (Number): The number of items at the end to skip before beginning
* the search.
*
* Returns the position of the last occurrence of `item` within the array or
* `-1` if `item` doesnt exist in the array.
**/
2008-12-11 10:31:36 +00:00
function lastIndexOf(item, i) {
i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
var n = this.slice(0, i).reverse().indexOf(item);
return (n < 0) ? n : i - n - 1;
}
2009-02-24 02:35:49 +00:00
// Replaces a built-in function. No PDoc needed.
2008-12-11 10:31:36 +00:00
function concat() {
var array = slice.call(this, 0), item;
for (var i = 0, length = arguments.length; i < length; i++) {
item = arguments[i];
if (Object.isArray(item) && !('callee' in item)) {
for (var j = 0, arrayLength = item.length; j < arrayLength; j++)
array.push(item[j]);
2007-01-18 22:24:27 +00:00
} else {
array.push(item);
2007-01-18 22:24:27 +00:00
}
}
return array;
2008-12-11 10:31:36 +00:00
}
Object.extend(arrayProto, Enumerable);
if (!arrayProto._reverse)
arrayProto._reverse = arrayProto.reverse;
Object.extend(arrayProto, {
_each: _each,
clear: clear,
first: first,
last: last,
compact: compact,
flatten: flatten,
without: without,
reverse: reverse,
reduce: reduce,
uniq: uniq,
intersect: intersect,
clone: clone,
toArray: clone,
size: size,
inspect: inspect,
toJSON: toJSON
});
// fix for opera
var CONCAT_ARGUMENTS_BUGGY = (function() {
return [].concat(arguments)[0][0] !== 1;
})(1,2)
if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat;
2008-12-11 10:31:36 +00:00
// use native browser JS 1.6 implementation if available
2008-12-11 16:00:45 +00:00
if (!arrayProto.indexOf) arrayProto.indexOf = indexOf;
if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf;
2008-12-11 10:31:36 +00:00
})();