larascript.js
Version:
Get the power of Laravel Utilities in Javascript
824 lines (647 loc) • 20.4 kB
JavaScript
export default class Collection {
constructor(collection) {
if (collection !== undefined && !Array.isArray(collection) && typeof collection !== 'object') {
this.items = [collection];
} else if (collection instanceof this.constructor) {
this.items = collection.all();
} else {
this.items = collection || [];
}
}
all() {
return this.items;
}
avg() {
const sum = this.items.reduce((acc, item) => acc + item, 0);
return sum / this.items.length;
}
chunk(size) {
const chunks = [];
for (let i = 0; i < this.items.length; i += size) {
chunks.push(this.items.slice(i, i + size));
}
return new Collection(chunks);
}
collapse() {
const flattened = this.items.reduce((acc, item) => acc.concat(item), []);
return new Collection(flattened);
}
combine(values) {
const combined = {};
this.items.forEach((key, index) => {
combined[key] = values[index];
});
return new Collection(combined);
}
concat(values) {
const concatenated = this.items.concat(values);
return new Collection(concatenated);
}
contains(value) {
return this.items.includes(value);
}
count() {
return this.items.length;
}
diff(values) {
const diff = this.items.filter((item) => !values.includes(item));
return new Collection(diff);
}
diffAssoc(values) {
const diff = {};
Object.keys(this.items).forEach((key) => {
if (!(key in values) || values[key] !== this.items[key]) {
diff[key] = this.items[key];
}
});
Object.keys(values).forEach((key) => {
if (!(key in this.items)) {
diff[key] = values[key];
}
});
return new Collection(diff);
}
diffKeys(values) {
const diff = {};
Object.keys(this.items).forEach((key) => {
if (!(key in values)) {
diff[key] = this.items[key];
}
});
return new Collection(diff);
}
dump() {
console.log(this.items);
return this;
}
each(callback) {
for (const [key, value] of Object.entries(this.items)) {
callback(value, key);
}
return this;
}
every(callback) {
for (const [key, value] of Object.entries(this.items)) {
if (!callback(value, key)) {
return false;
}
}
return true;
}
except(keys) {
const filtered = {};
for (const [key, value] of Object.entries(this.items)) {
if (!keys.includes(key)) {
filtered[key] = value;
}
}
return new Collection(filtered);
}
filter(callback) {
const filtered = {};
for (const [key, value] of Object.entries(this.items)) {
if (callback(value, key)) {
filtered[key] = value;
}
}
return new Collection(filtered);
}
first(callback) {
if (callback) {
for (const [key, value] of Object.entries(this.items)) {
if (callback(value, key)) {
return value;
}
}
return null;
}
return this.items[0] || null;
}
firstWhere(key, operator, value) {
const operators = {
'=': (a, b) => a === b,
'!=': (a, b) => a !== b,
'>': (a, b) => a > b,
'>=': (a, b) => a >= b,
'<': (a, b) => a < b,
'<=': (a, b) => a <= b,
};
const callback = operators[operator] || ((a, b) => a === b);
for (const [k, v] of Object.entries(this.items)) {
if (callback(v[key], value)) {
return v;
}
}
return null;
}
flatMap(callback) {
const mapped = this.items.map(callback);
const flattened = Array.prototype.concat(...mapped);
return new Collection(flattened);
}
flatten(depth = Infinity) {
const flattened = [];
function flattenRecursive(value, depth) {
if (depth === 0 || !Array.isArray(value)) {
flattened.push(value);
return;
}
for (const item of value) {
flattenRecursive(item, depth - 1);
}
}
flattenRecursive(this.items, depth);
return new Collection(flattened);
}
flip() {
const flipped = {};
for (const [key, value] of Object.entries(this.items)) {
flipped[value] = key;
}
return new Collection(flipped);
}
forget(key) {
delete this.items[key];
return this;
}
forPage(page, perPage) {
const start = (page - 1) * perPage;
const sliced = Object.entries(this.items).slice(start, start + perPage);
const paginated = {};
for (const [key, value] of sliced) {
paginated[key] = value;
}
return new Collection(paginated);
}
get(key, defaultValue = null) {
return this.items.hasOwnProperty(key) ? this.items[key] : defaultValue;
}
groupBy(callback) {
const groups = {};
for (const [key, value] of Object.entries(this.items)) {
const groupKey = callback(value, key);
if (!groups[groupKey]) {
groups[groupKey] = [];
}
groups[groupKey].push(value);
}
for (const key in groups) {
groups[key] = new Collection(groups[key]);
}
return new Collection(groups);
}
has(key) {
return this.items.hasOwnProperty(key);
}
implode(separator, key) {
let values = Object.values(this.items);
if (key) {
values = values.map((item) => item[key]);
}
return values.join(separator);
}
intersect(values) {
const intersected = {};
for (const key in this.items) {
if (key in values && this.items[key] === values[key]) {
intersected[key] = this.items[key];
}
}
return new Collection(intersected);
}
isEmpty() {
return Object.keys(this.items).length === 0;
}
isNotEmpty() {
return !this.isEmpty();
}
keyBy(key) {
const keyed = {};
for (const item of Object.values(this.items)) {
keyed[item[key]] = item;
}
return new Collection(keyed);
}
keys() {
return new Collection(Object.keys(this.items));
}
last() {
return this.items[Object.keys(this.items).pop()];
}
map(callback) {
const mappedItems = this.items.map(callback);
return new Collection(mappedItems);
}
mapWithKeys(callback) {
let keys = Object.keys(this.items);
let result = {};
keys.forEach(key => {
let [newKey, value] = callback(this.items[key], key);
result[newKey] = value;
});
return new Collection(result);
}
max(key) {
const values = Object.values(this.items);
if (values.length === 0) {
return null;
}
if (key) {
return Math.max(...values.map((item) => item[key]));
}
return Math.max(...values);
}
median(key) {
const values = Object.values(this.items);
if (values.length === 0) {
return null;
}
if (key) {
values.sort((a, b) => a[key] - b[key]);
} else {
values.sort((a, b) => a - b);
}
const middle = Math.floor(values.length / 2);
if (values.length % 2 === 0) {
return (values[middle - 1] + values[middle]) / 2;
}
return values[middle];
}
merge(values) {
return new Collection({...this.items, ...values});
}
mergeRecursive(values) {
const merge = (target, source) => {
Object.keys(source).forEach((key) => {
const sourceValue = source[key];
const targetValue = target[key];
if (typeof sourceValue === 'object' && sourceValue !== null) {
if (typeof targetValue === 'object' && targetValue !== null) {
merge(targetValue, sourceValue);
} else {
target[key] = sourceValue;
}
} else {
target[key] = sourceValue;
}
});
};
merge(this.items, values);
return new Collection(this.items);
}
min(key) {
const values = Object.values(this.items);
if (values.length === 0) {
return null;
}
if (key) {
return Math.min(...values.map((item) => item[key]));
}
return Math.min(...values);
}
mode(key) {
const values = Object.values(this.items);
if (values.length === 0) {
return null;
}
const frequencies = {};
for (const item of values) {
const value = key ? item[key] : item;
frequencies[value] = (frequencies[value] || 0) + 1;
}
const modes = [];
let maxFrequency = 0;
for (const [value, frequency] of Object.entries(frequencies)) {
if (frequency > maxFrequency) {
modes.length = 0;
modes.push(value);
maxFrequency = frequency;
} else if (frequency === maxFrequency) {
modes.push(value);
}
}
if (modes.length === 1) {
return modes[0];
}
return modes;
}
nth(n, offset = 0) {
const values = Object.values(this.items);
if (values.length === 0) {
return null;
}
return values[n + offset] ?? null;
}
only(keys) {
const filtered = {};
for (const key of keys) {
if (key in this.items) {
filtered[key] = this.items[key];
}
}
return new Collection(filtered);
}
pad(length, value) {
const count = Math.max(0, length - Object.keys(this.items).length);
const padded = {...this.items};
for (let i = 0; i < count; i++) {
const key = i + Object.keys(this.items).length;
padded[key] = value;
}
return new Collection(padded);
}
partition(callback) {
const partitions = [[], []];
for (const [key, value] of Object.entries(this.items)) {
if (callback(value, key)) {
partitions[0].push(value);
} else {
partitions[1].push(value);
}
}
return new Collection(partitions);
}
pipe(callbacks) {
return callbacks.reduce((collection, callback) => callback(collection), this);
}
pluck(key) {
return new Collection(Object.values(this.items).map((item) => item[key]));
}
pop() {
const items = {...this.items};
const keys = Object.keys(items);
const lastKey = keys[keys.length - 1];
delete items[lastKey];
return [items[lastKey], new Collection(items)];
}
prepend(value, key = null) {
const items = {...this.items};
const keys = Object.keys(items);
if (key !== null) {
items[key] = value;
} else if (keys.length === 0) {
items[0] = value;
} else {
const lastKey = keys[keys.length - 1];
const newKey = parseInt(lastKey, 10) + 1;
items[newKey] = value;
}
return new Collection(items);
}
pull(key) {
const items = {...this.items};
const value = items[key];
delete items[key];
return [value, new Collection(items)];
}
push(value, key = null) {
const items = {...this.items};
const keys = Object.keys(items);
if (key !== null) {
items[key] = value;
} else if (keys.length === 0) {
items[0] = value;
} else {
const lastKey = keys[keys.length - 1];
const newKey = parseInt(lastKey, 10) + 1;
items[newKey] = value;
}
return new Collection(items);
}
random(keys = null) {
const items = Object.values(this.items);
const index = Math.floor(Math.random() * items.length);
if (keys !== null) {
return keys.reduce((result, key) => {
result[key] = items[index][key];
return result;
}, {});
}
return items[index];
}
reduce(callback, initial = null) {
const values = Object.values(this.items);
let accumulator = initial !== null ? initial : values.shift();
for (const value of values) {
accumulator = callback(accumulator, value);
}
return accumulator;
}
reject(callback) {
const filtered = {};
for (const [key, value] of Object.entries(this.items)) {
if (!callback(value, key)) {
filtered[key] = value;
}
}
return new Collection(filtered);
}
reverse() {
const items = Object.values(this.items).reverse();
return new Collection(items);
}
search(value, strict = false) {
const items = Object.values(this.items);
const index = items.findIndex((item) => (strict ? item === value : item == value));
return index !== -1 ? index : null;
}
shift() {
const items = {...this.items};
const firstKey = Object.keys(items)[0];
delete items[firstKey];
return [this.items[firstKey], new Collection(items)];
}
shuffle() {
const items = Object.values(this.items);
for (let i = items.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[items[i], items[j]] = [items[j], items[i]];
}
const shuffled = {};
items.forEach((item, index) => {
shuffled[index] = item;
});
return new Collection(shuffled);
}
slice(start, length = null) {
const items = {...this.items};
const keys = Object.keys(items);
if (start < 0) {
start = Math.max(0, keys.length + start);
}
if (length === null) {
length = keys.length - start;
}
const sliced = {};
keys.slice(start, start + length).forEach((key, index) => {
sliced[index] = items[key];
});
return new Collection(sliced);
}
sort(callback = null) {
const items = Object.values(this.items);
let sorted = null;
if (callback === null) {
sorted = items.sort();
} else {
sorted = items.sort(callback);
}
const result = {};
sorted.forEach((item, index) => {
result[index] = item;
});
return new Collection(result);
}
sortBy(key, options = null) {
const items = Object.values(this.items);
const sorted = items.sort((a, b) => {
if (options !== null && options.natural === true) {
a = a[key].toLowerCase();
b = b[key].toLowerCase();
}
if (a[key] < b[key]) {
return -1;
}
if (a[key] > b[key]) {
return 1;
}
return 0;
});
const result = {};
sorted.forEach((item, index) => {
result[index] = item;
});
return new Collection(result);
}
splice(index, length = 1, replacement = null) {
const items = {...this.items};
const keys = Object.keys(items);
if (index < 0) {
index = Math.max(0, keys.length + index);
}
const removed = {};
keys.slice(index, index + length).forEach((key, i) => {
removed[i] = items[key];
delete items[key];
});
if (replacement !== null) {
if (typeof replacement === 'function') {
replacement = replacement(new Collection(removed)).all();
}
let position = index;
for (const [key, value] of Object.entries(replacement)) {
items[position] = value;
position++;
}
}
return [new Collection(removed), new Collection(items)];
}
sum(key = null) {
const items = Object.values(this.items);
if (key !== null) {
return items.reduce((accumulator, item) => accumulator + item[key], 0);
}
return items.reduce((accumulator, item) => accumulator + item, 0);
}
take(length) {
const items = {...this.items};
if (length < 0) {
length = 0;
}
const taken = {};
let index = 0;
for (const [key, value] of Object.entries(items)) {
if (index >= length) {
break;
}
taken[key] = value;
index++;
}
return new Collection(taken);
}
tap(callback) {
callback(new Collection(this.items));
return this;
}
toArray() {
return Object.values(this.items);
}
toJson() {
return JSON.stringify(this.items);
}
transform(callback, initial = null) {
const items = {...this.items};
let result = initial;
for (const [key, value] of Object.entries(items)) {
result = callback(result, value, key);
}
return result;
}
unique(key = null) {
const items = {...this.items};
const unique = {};
const uniqueValues = [];
for (const [key, value] of Object.entries(items)) {
let compareValue = value;
if (key !== null && typeof value === 'object' && value.hasOwnProperty(key)) {
compareValue = value[key];
}
if (!uniqueValues.includes(compareValue)) {
unique[key] = value;
uniqueValues.push(compareValue);
}
}
return new Collection(unique);
}
values() {
return new Collection(Object.values(this.items));
}
when(condition, callback) {
if (condition) {
return callback(new Collection(this.items));
}
return this;
}
where(key, operator, value = null) {
const items = {...this.items};
let filtered = {};
if (value === null) {
filtered = Object.entries(items).filter(([, item]) => item === operator);
} else {
filtered = Object.entries(items).filter(([, item]) => {
switch (operator) {
case '=':
return item[key] === value;
case '<':
return item[key] < value;
case '<=':
return item[key] <= value;
case '>':
return item[key] > value;
case '>=':
return item[key] >= value;
case '!=':
return item[key] !== value;
default:
return false;
}
});
}
const result = {};
filtered.forEach(([index, value]) => {
result[index] = value;
});
return new Collection(result);
}
zip(...collections) {
const zipped = {};
for (const [key, value] of Object.entries(this.items)) {
const items = [value];
collections.forEach((collection) => {
items.push(collection.get(key, null));
});
zipped[key] = items;
}
return new Collection(zipped);
}
}