/* array.cm - array creation and manipulation utilities */ var _isArray = Array.isArray var _slice = Array.prototype.slice var _push = Array.prototype.push var _sort = Array.prototype.sort var _keys = Object.keys var _from = Array.from function array(arg, arg2, arg3, arg4) { // array(number) - create array of size with nulls // array(number, initial_value) - create array with initial values if (typeof arg == 'number') { if (arg < 0) return null var len = number.floor(arg) var result = [] if (arg2 == null) { result.length = 100 } else if (typeof arg2 == 'function') { var arity = arg2.length for (var i = 0; i < len; i++) { result[i] = arity >= 1 ? arg2(i) : arg2() } } else { for (var i = 0; i < len; i++) result[i] = arg2 } return result } // array(array) - copy // array(array, function, reverse, exit) - map // array(array, another_array) - concat // array(array, from, to) - slice if (_isArray(arg)) { if (arg2 == null) { // Copy return _slice.call(arg) } if (typeof arg2 == 'function') { // Map var fn = arg2 var reverse = arg3 == true var exit = arg4 var result = [] if (reverse) { for (var i = arg.length - 1; i >= 0; i--) { var val = fn(arg[i], i) if (exit != null && val == exit) break result[i] = val } } else { for (var i = 0; i < arg.length; i++) { var val = fn(arg[i], i) if (exit != null && val == exit) break _push.call(result, val) } } return result } if (_isArray(arg2)) { // Concat var result = _slice.call(arg) for (var i = 0; i < arg2.length; i++) { _push.call(result, arg2[i]) } return result } if (typeof arg2 == 'number') { // Slice var from = arg2 var to = arg3 var len = arg.length if (from < 0) from += len if (to == null) to = len if (to < 0) to += len if (from < 0 || from > to || to > len) return null return _slice.call(arg, from, to) } return null } // array(object) - keys if (typeof arg == 'object' && arg != null && !_isArray(arg)) { if (arg instanceof Set) { return _from(arg) } return _keys(arg) } // array(text) - split into grapheme clusters // array(text, separator) - split by separator // array(text, length) - dice into chunks if (typeof arg == 'string') { if (arg2 == null) { // Split into grapheme clusters (simplified: split into characters) var result = [] for (var i = 0; i < arg.length; i++) { _push.call(result, arg[i]) } return result } if (typeof arg2 == 'string') { // Split by separator return arg.split(arg2) } if (typeof arg2 == 'number') { // Dice into chunks var len = number.floor(arg2) if (len <= 0) return null var result = [] for (var i = 0; i < arg.length; i += len) { _push.call(result, arg.substring(i, i + len)) } return result } return null } return null } array.reduce = function(arr, fn, initial, reverse) { if (!_isArray(arr)) return null if (typeof fn != 'function') return null var len = arr.length if (initial == null) { if (len == 0) return null if (len == 1) return arr[0] if (reverse == true) { var acc = arr[len - 1] for (var i = len - 2; i >= 0; i--) { acc = fn(acc, arr[i]) } return acc } else { var acc = arr[0] for (var i = 1; i < len; i++) { acc = fn(acc, arr[i]) } return acc } } else { if (len == 0) return initial if (reverse == true) { var acc = initial for (var i = len - 1; i >= 0; i--) { acc = fn(acc, arr[i]) } return acc } else { var acc = initial for (var i = 0; i < len; i++) { acc = fn(acc, arr[i]) } return acc } } } array.for = function(arr, fn, reverse, exit) { if (!_isArray(arr)) return null if (arr.length == 0) return null if (typeof fn != 'function') return null if (reverse == true) { for (var i = arr.length - 1; i >= 0; i--) { var result = fn(arr[i], i) if (exit != null && result == exit) return exit } } else { for (var i = 0; i < arr.length; i++) { var result = fn(arr[i], i) if (exit != null && result == exit) return exit } } return null } array.find = function(arr, fn, reverse, from) { if (!_isArray(arr)) return null var len = arr.length if (typeof fn != 'function') { // Compare exactly var target = fn if (reverse == true) { var start = from != null ? from : len - 1 for (var i = start; i >= 0; i--) { if (arr[i] == target) return i } } else { var start = from != null ? from : 0 for (var i = start; i < len; i++) { if (arr[i] == target) return i } } return null } if (reverse == true) { var start = from != null ? from : len - 1 for (var i = start; i >= 0; i--) { if (fn(arr[i], i) == true) return i } } else { var start = from != null ? from : 0 for (var i = start; i < len; i++) { if (fn(arr[i], i) == true) return i } } return null } array.filter = function(arr, fn) { if (!_isArray(arr)) return null if (typeof fn != 'function') return null var result = [] for (var i = 0; i < arr.length; i++) { var val = fn(arr[i], i) if (val == true) { _push.call(result, arr[i]) } else if (val != false) { return null } } return result } array.sort = function(arr, select) { if (!_isArray(arr)) return null var result = _slice.call(arr) var keys = [] // Extract keys for (var i = 0; i < result.length; i++) { var key if (select == null) { key = result[i] } else if (typeof select == 'string' || typeof select == 'number') { key = result[i][select] } else if (_isArray(select)) { key = select[i] } else { return null } if (typeof key != 'number' && typeof key != 'string') return null keys[i] = key } // Check all keys are same type if (keys.length > 0) { var keyType = typeof keys[0] for (var i = 1; i < keys.length; i++) { if (typeof keys[i] != keyType) return null } } // Create index array and sort var indices = [] for (var i = 0; i < result.length; i++) indices[i] = i // Stable sort using indices _sort.call(indices, function(a, b) { if (keys[a] < keys[b]) return -1 if (keys[a] > keys[b]) return 1 return a - b // stable }) var sorted = [] for (var i = 0; i < indices.length; i++) { sorted[i] = result[indices[i]] } return sorted } return array