Files
cell/internal/array.cm
2025-12-28 19:50:27 -06:00

304 lines
6.8 KiB
Plaintext

/* 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