module.exports = { join: function(arr, separator, prop) { if (!Array.isArray(arr)) return arr; return arr.map(item => prop ? item[prop] : item).join(separator); }, first: function(arr) { if (!Array.isArray(arr)) return arr; return arr.length ? arr[0] : null; }, top: function(arr, count) { if (!Array.isArray(arr)) return arr; return arr.slice(0, count); }, last: function(arr, count) { if (!Array.isArray(arr)) return arr; if (count) return arr.slice(-count); return arr[arr.length - 1]; }, where: function(arr, prop, operator, value) { if (!Array.isArray(arr)) return arr; return arr.filter(item => { let val = item[prop]; switch (operator) { case 'startsWith': return String(val).startsWith(value); case 'endsWith': return String(val).endsWith(value); case 'contains': return String(val).includes(value); case '===': return val === value; case '!==': return val !== value; case '==': return val == value; case '!=': return val != value; case '<=': return val <= value; case '>=': return val >= value; case '<': return val < value; case '>': return val > value; }; return true; }); }, unique: function(arr, prop) { if (!Array.isArray(arr)) return arr; if (prop) arr = arr.map(item => item[prop]); let lookup = []; return arr.filter(item => { if (lookup.includes(item)) return false; lookup.push(item); return true; }); }, groupBy: function(arr, prop) { if (!Array.isArray(arr)) return arr; return arr.reduce((groups, item) => { let group = String(item[prop]); if (!groups[group]) groups[group] = []; groups[group].push(item); return groups; }, {}); }, sort: function(arr, prop) { if (!Array.isArray(arr)) return arr; return arr.slice(0).sort((a, b) => { a = prop ? a && a[prop] : a; b = prop ? b && b[prop] : b; if (typeof a == 'string' && typeof b == 'string') { return a.localeCompare(b); } return a < b ? -1 : a > b ? 1 : 0; }); }, randomize: function(arr) { if (!Array.isArray(arr)) return arr; arr = arr.slice(0); let len = arr.length; if (len) { while (--len) { let rnd = Math.floor(Math.random() * (len + 1)); let val = arr[len]; arr[len] = arr[rnd]; arr[rnd] = val; } } return arr; }, reverse: function(arr) { if (!Array.isArray(arr)) return arr; return arr.slice(0).reverse(); }, count: function(arr) { if (!Array.isArray(arr)) return arr; return arr.length; }, min: function(arr, prop) { if (!Array.isArray(arr)) return arr; return arr.reduce((min, num) => { num = prop ? num[prop] : num; if (num == null) return min; num = Number(num); if (isNaN(num) || !isFinite(num)) return min; return min == null || num < min ? num : min; }, null); }, max: function(arr, prop) { if (!Array.isArray(arr)) return arr; return arr.reduce((max, num) => { num = prop ? num[prop] : num; if (num == null) return max; num = Number(num); if (isNaN(num) || !isFinite(num)) return max; return max == null || num > max ? num : max; }, null); }, sum: function(arr, prop) { if (!Array.isArray(arr)) return arr; return arr.reduce((sum, num) => { num = prop ? num[prop] : num; if (num == null) return sum; num = Number(num); if (isNaN(num) || !isFinite(num)) return sum; return sum + num; }, 0); }, avg: function(arr, prop) { if (!Array.isArray(arr)) return arr; let cnt = 0; return arr.reduce((avg, num) => { num = prop ? num[prop] : num; if (num == null) return avg; num = Number(num); if (isNaN(num) || !isFinite(num)) return avg; cnt++; return avg + num; }, 0) / cnt; }, flatten: function(arr, prop) { if (!Array.isArray(arr)) return arr; return arr.map(item => item[prop]); }, keys: function(obj) { if (typeof obj != 'object') return obj; return Object.keys(obj); }, values: function(obj) { if (typeof obj != 'object') return obj; return Object.values(obj); }, };