2023-01-05 07:17:44 +03:00
|
|
|
import type { SortObjectKeys } from './types';
|
|
|
|
|
|
|
|
function getLength(type: string, collection: unknown) {
|
2018-12-21 22:18:05 +03:00
|
|
|
if (type === 'Object') {
|
2021-10-21 22:08:35 +03:00
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
|
|
return Object.keys(collection as {}).length;
|
2018-12-21 22:18:05 +03:00
|
|
|
} else if (type === 'Array') {
|
2020-08-22 03:13:24 +03:00
|
|
|
return (collection as unknown[]).length;
|
2018-12-21 22:18:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return Infinity;
|
|
|
|
}
|
|
|
|
|
2023-01-05 07:17:44 +03:00
|
|
|
function isIterableMap(collection: unknown) {
|
|
|
|
return typeof (collection as Map<unknown, unknown>).set === 'function';
|
2018-12-21 22:18:05 +03:00
|
|
|
}
|
|
|
|
|
2020-08-22 03:13:24 +03:00
|
|
|
function getEntries(
|
|
|
|
type: string,
|
|
|
|
collection: any,
|
2023-01-05 07:17:44 +03:00
|
|
|
sortObjectKeys: SortObjectKeys,
|
2020-08-22 03:13:24 +03:00
|
|
|
from = 0,
|
2023-07-12 21:03:20 +03:00
|
|
|
to = Infinity,
|
2023-01-05 07:17:44 +03:00
|
|
|
): { entries: { key: string | number; value: unknown }[]; hasMore?: boolean } {
|
2018-12-21 22:18:05 +03:00
|
|
|
let res;
|
|
|
|
|
|
|
|
if (type === 'Object') {
|
|
|
|
let keys = Object.getOwnPropertyNames(collection);
|
|
|
|
|
|
|
|
if (sortObjectKeys) {
|
|
|
|
keys.sort(sortObjectKeys === true ? undefined : sortObjectKeys);
|
|
|
|
}
|
|
|
|
|
|
|
|
keys = keys.slice(from, to + 1);
|
|
|
|
|
|
|
|
res = {
|
2020-08-08 23:26:39 +03:00
|
|
|
entries: keys.map((key) => ({ key, value: collection[key] })),
|
2018-12-21 22:18:05 +03:00
|
|
|
};
|
|
|
|
} else if (type === 'Array') {
|
|
|
|
res = {
|
|
|
|
entries: collection
|
|
|
|
.slice(from, to + 1)
|
2020-08-22 03:13:24 +03:00
|
|
|
.map((val: unknown, idx: number) => ({ key: idx + from, value: val })),
|
2018-12-21 22:18:05 +03:00
|
|
|
};
|
|
|
|
} else {
|
|
|
|
let idx = 0;
|
|
|
|
const entries = [];
|
|
|
|
let done = true;
|
|
|
|
|
|
|
|
const isMap = isIterableMap(collection);
|
|
|
|
|
|
|
|
for (const item of collection) {
|
|
|
|
if (idx > to) {
|
|
|
|
done = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (from <= idx) {
|
|
|
|
if (isMap && Array.isArray(item)) {
|
|
|
|
if (typeof item[0] === 'string' || typeof item[0] === 'number') {
|
|
|
|
entries.push({ key: item[0], value: item[1] });
|
|
|
|
} else {
|
|
|
|
entries.push({
|
|
|
|
key: `[entry ${idx}]`,
|
|
|
|
value: {
|
|
|
|
'[key]': item[0],
|
2020-08-08 23:26:39 +03:00
|
|
|
'[value]': item[1],
|
|
|
|
},
|
2018-12-21 22:18:05 +03:00
|
|
|
});
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
entries.push({ key: idx, value: item });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
idx++;
|
|
|
|
}
|
|
|
|
|
|
|
|
res = {
|
|
|
|
hasMore: !done,
|
2020-08-08 23:26:39 +03:00
|
|
|
entries,
|
2018-12-21 22:18:05 +03:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2020-08-22 03:13:24 +03:00
|
|
|
function getRanges(from: number, to: number, limit: number) {
|
2018-12-21 22:18:05 +03:00
|
|
|
const ranges = [];
|
|
|
|
while (to - from > limit * limit) {
|
|
|
|
limit = limit * limit;
|
|
|
|
}
|
|
|
|
for (let i = from; i <= to; i += limit) {
|
|
|
|
ranges.push({ from: i, to: Math.min(to, i + limit - 1) });
|
|
|
|
}
|
|
|
|
|
|
|
|
return ranges;
|
|
|
|
}
|
|
|
|
|
|
|
|
export default function getCollectionEntries(
|
2020-08-22 03:13:24 +03:00
|
|
|
type: string,
|
2023-01-05 07:17:44 +03:00
|
|
|
collection: unknown,
|
|
|
|
sortObjectKeys: SortObjectKeys,
|
2020-08-22 03:13:24 +03:00
|
|
|
limit: number,
|
2018-12-21 22:18:05 +03:00
|
|
|
from = 0,
|
2023-07-12 21:03:20 +03:00
|
|
|
to = Infinity,
|
2018-12-21 22:18:05 +03:00
|
|
|
) {
|
|
|
|
const getEntriesBound = getEntries.bind(
|
|
|
|
null,
|
|
|
|
type,
|
|
|
|
collection,
|
2023-07-12 21:03:20 +03:00
|
|
|
sortObjectKeys,
|
2018-12-21 22:18:05 +03:00
|
|
|
);
|
|
|
|
|
|
|
|
if (!limit) {
|
|
|
|
return getEntriesBound().entries;
|
|
|
|
}
|
|
|
|
|
|
|
|
const isSubset = to < Infinity;
|
|
|
|
const length = Math.min(to - from, getLength(type, collection));
|
|
|
|
|
|
|
|
if (type !== 'Iterable') {
|
|
|
|
if (length <= limit || limit < 7) {
|
|
|
|
return getEntriesBound(from, to).entries;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (length <= limit && !isSubset) {
|
|
|
|
return getEntriesBound(from, to).entries;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let limitedEntries;
|
|
|
|
if (type === 'Iterable') {
|
|
|
|
const { hasMore, entries } = getEntriesBound(from, from + limit - 1);
|
|
|
|
|
|
|
|
limitedEntries = hasMore
|
|
|
|
? [...entries, ...getRanges(from + limit, from + 2 * limit - 1, limit)]
|
|
|
|
: entries;
|
|
|
|
} else {
|
|
|
|
limitedEntries = isSubset
|
|
|
|
? getRanges(from, to, limit)
|
|
|
|
: [
|
|
|
|
...getEntriesBound(0, limit - 5).entries,
|
|
|
|
...getRanges(limit - 4, length - 5, limit),
|
2020-08-08 23:26:39 +03:00
|
|
|
...getEntriesBound(length - 4, length - 1).entries,
|
2018-12-21 22:18:05 +03:00
|
|
|
];
|
|
|
|
}
|
|
|
|
|
|
|
|
return limitedEntries;
|
|
|
|
}
|