@mmstack/primitives 22.8.2 → 22.8.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -2005,6 +2005,29 @@ function indexArray(source, map, opt = {}) {
|
|
|
2005
2005
|
*/
|
|
2006
2006
|
const mapArray = indexArray;
|
|
2007
2007
|
|
|
2008
|
+
function effectiveKeys(arr, getKey, report) {
|
|
2009
|
+
const out = new Array(arr.length);
|
|
2010
|
+
const counts = new Map();
|
|
2011
|
+
const firstOrder = [];
|
|
2012
|
+
let duped = null;
|
|
2013
|
+
for (let i = 0; i < arr.length; i++) {
|
|
2014
|
+
const base = String(getKey(arr[i]));
|
|
2015
|
+
const seen = counts.get(base) ?? 0;
|
|
2016
|
+
if (seen === 0) {
|
|
2017
|
+
firstOrder.push(base);
|
|
2018
|
+
}
|
|
2019
|
+
else {
|
|
2020
|
+
(duped ??= new Set()).add(base);
|
|
2021
|
+
}
|
|
2022
|
+
out[i] = seen === 0 ? base : base + '#' + seen;
|
|
2023
|
+
counts.set(base, seen + 1);
|
|
2024
|
+
}
|
|
2025
|
+
if (report && duped) {
|
|
2026
|
+
const bases = firstOrder.filter((b) => duped.has(b));
|
|
2027
|
+
untracked(() => report(bases));
|
|
2028
|
+
}
|
|
2029
|
+
return out;
|
|
2030
|
+
}
|
|
2008
2031
|
/**
|
|
2009
2032
|
* Reactively maps items from a source array to a new array by value (identity).
|
|
2010
2033
|
*
|
|
@@ -2018,11 +2041,15 @@ const mapArray = indexArray;
|
|
|
2018
2041
|
* when the list is reordered.
|
|
2019
2042
|
*
|
|
2020
2043
|
* @param source A `Signal<T[]>` or a function returning `T[]`.
|
|
2021
|
-
* @param mapFn The mapping function. Receives the item
|
|
2044
|
+
* @param mapFn The mapping function. Receives the item, its index as a Signal, and the
|
|
2045
|
+
* entry's key (the effective ordinal key when `duplicateKeys` is enabled, otherwise
|
|
2046
|
+
* the raw key) — stable for the lifetime of the mapped entry.
|
|
2022
2047
|
* @param options Optional configuration:
|
|
2023
2048
|
* - `onDestroy`: A callback invoked when a mapped item is removed from the array.
|
|
2024
2049
|
* - `key`: A custom key extractor for identity matching (e.g. `(item) => item.id`)
|
|
2025
2050
|
* when item references change but conceptual identity is preserved.
|
|
2051
|
+
* - `duplicateKeys`: Opt-in policy for handling duplicate keys. When omitted,
|
|
2052
|
+
* behavior is unchanged (duplicates collapse). See {@link DuplicateKeyPolicy}.
|
|
2026
2053
|
* @returns A `Signal<U[]>` containing the mapped array.
|
|
2027
2054
|
*
|
|
2028
2055
|
* @example
|
|
@@ -2057,9 +2084,11 @@ function keyArray(source, mapFn, options = {}) {
|
|
|
2057
2084
|
const tempIndexes = [];
|
|
2058
2085
|
const newIndicesNext = [];
|
|
2059
2086
|
const newIndexesCache = new Array();
|
|
2087
|
+
const dup = options.duplicateKeys;
|
|
2060
2088
|
return computed(() => {
|
|
2061
2089
|
const newItems = sourceSignal() || [];
|
|
2062
2090
|
return untracked(() => {
|
|
2091
|
+
const newKeys = dup ? effectiveKeys(newItems, getKey, dup.report) : null;
|
|
2063
2092
|
let i;
|
|
2064
2093
|
let j;
|
|
2065
2094
|
const newLen = newItems.length;
|
|
@@ -2092,7 +2121,7 @@ function keyArray(source, mapFn, options = {}) {
|
|
|
2092
2121
|
const indexSignal = signal(j, /* @ts-ignore */
|
|
2093
2122
|
...(ngDevMode ? [{ debugName: "indexSignal" }] : /* istanbul ignore next */ []));
|
|
2094
2123
|
newIndexes[j] = indexSignal;
|
|
2095
|
-
newMapped[j] = mapFn(item, indexSignal);
|
|
2124
|
+
newMapped[j] = mapFn(item, indexSignal, newKeys ? newKeys[j] : getKey(item));
|
|
2096
2125
|
}
|
|
2097
2126
|
}
|
|
2098
2127
|
else {
|
|
@@ -2100,26 +2129,29 @@ function keyArray(source, mapFn, options = {}) {
|
|
|
2100
2129
|
temp.length = 0;
|
|
2101
2130
|
tempIndexes.length = 0;
|
|
2102
2131
|
newIndicesNext.length = 0;
|
|
2103
|
-
|
|
2132
|
+
const oldKeys = dup ? effectiveKeys(items, getKey) : null;
|
|
2133
|
+
const kNew = newKeys
|
|
2134
|
+
? (idx) => newKeys[idx]
|
|
2135
|
+
: (idx) => getKey(newItems[idx]);
|
|
2136
|
+
const kOld = oldKeys
|
|
2137
|
+
? (idx) => oldKeys[idx]
|
|
2138
|
+
: (idx) => getKey(items[idx]);
|
|
2139
|
+
for (start = 0, end = Math.min(len, newLen); start < end && kOld(start) === kNew(start); start++) {
|
|
2104
2140
|
newMapped[start] = mapped[start];
|
|
2105
2141
|
newIndexes[start] = indexes[start];
|
|
2106
2142
|
}
|
|
2107
|
-
for (end = len - 1, newEnd = newLen - 1; end >= start &&
|
|
2108
|
-
newEnd >= start &&
|
|
2109
|
-
getKey(items[end]) === getKey(newItems[newEnd]); end--, newEnd--) {
|
|
2143
|
+
for (end = len - 1, newEnd = newLen - 1; end >= start && newEnd >= start && kOld(end) === kNew(newEnd); end--, newEnd--) {
|
|
2110
2144
|
temp[newEnd] = mapped[end];
|
|
2111
2145
|
tempIndexes[newEnd] = indexes[end];
|
|
2112
2146
|
}
|
|
2113
2147
|
for (j = newEnd; j >= start; j--) {
|
|
2114
|
-
|
|
2115
|
-
key = getKey(item);
|
|
2148
|
+
key = kNew(j);
|
|
2116
2149
|
i = newIndices.get(key) ?? -1;
|
|
2117
2150
|
newIndicesNext[j] = i === undefined ? -1 : i;
|
|
2118
2151
|
newIndices.set(key, j);
|
|
2119
2152
|
}
|
|
2120
2153
|
for (i = start; i <= end; i++) {
|
|
2121
|
-
|
|
2122
|
-
key = getKey(item);
|
|
2154
|
+
key = kOld(i);
|
|
2123
2155
|
j = newIndices.get(key) ?? -1;
|
|
2124
2156
|
if (j !== undefined && j !== -1) {
|
|
2125
2157
|
temp[j] = mapped[i];
|
|
@@ -2143,7 +2175,7 @@ function keyArray(source, mapFn, options = {}) {
|
|
|
2143
2175
|
const indexSignal = signal(j, /* @ts-ignore */
|
|
2144
2176
|
...(ngDevMode ? [{ debugName: "indexSignal" }] : /* istanbul ignore next */ []));
|
|
2145
2177
|
newIndexes[j] = indexSignal;
|
|
2146
|
-
newMapped[j] = mapFn(newItems[j], indexSignal);
|
|
2178
|
+
newMapped[j] = mapFn(newItems[j], indexSignal, kNew(j));
|
|
2147
2179
|
}
|
|
2148
2180
|
}
|
|
2149
2181
|
items.length = newLen;
|