@mmstack/primitives 20.13.2 → 20.13.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.
- package/fesm2022/mmstack-primitives.mjs +45 -13
- package/fesm2022/mmstack-primitives.mjs.map +1 -1
- package/index.d.ts +35 -3
- package/package.json +1 -1
|
@@ -2010,6 +2010,29 @@ function indexArray(source, map, opt = {}) {
|
|
|
2010
2010
|
*/
|
|
2011
2011
|
const mapArray = indexArray;
|
|
2012
2012
|
|
|
2013
|
+
function effectiveKeys(arr, getKey, report) {
|
|
2014
|
+
const out = new Array(arr.length);
|
|
2015
|
+
const counts = new Map();
|
|
2016
|
+
const firstOrder = [];
|
|
2017
|
+
let duped = null;
|
|
2018
|
+
for (let i = 0; i < arr.length; i++) {
|
|
2019
|
+
const base = String(getKey(arr[i]));
|
|
2020
|
+
const seen = counts.get(base) ?? 0;
|
|
2021
|
+
if (seen === 0) {
|
|
2022
|
+
firstOrder.push(base);
|
|
2023
|
+
}
|
|
2024
|
+
else {
|
|
2025
|
+
(duped ??= new Set()).add(base);
|
|
2026
|
+
}
|
|
2027
|
+
out[i] = seen === 0 ? base : base + '#' + seen;
|
|
2028
|
+
counts.set(base, seen + 1);
|
|
2029
|
+
}
|
|
2030
|
+
if (report && duped) {
|
|
2031
|
+
const bases = firstOrder.filter((b) => duped.has(b));
|
|
2032
|
+
untracked(() => report(bases));
|
|
2033
|
+
}
|
|
2034
|
+
return out;
|
|
2035
|
+
}
|
|
2013
2036
|
/**
|
|
2014
2037
|
* Reactively maps items from a source array to a new array by value (identity).
|
|
2015
2038
|
*
|
|
@@ -2023,11 +2046,15 @@ const mapArray = indexArray;
|
|
|
2023
2046
|
* when the list is reordered.
|
|
2024
2047
|
*
|
|
2025
2048
|
* @param source A `Signal<T[]>` or a function returning `T[]`.
|
|
2026
|
-
* @param mapFn The mapping function. Receives the item
|
|
2049
|
+
* @param mapFn The mapping function. Receives the item, its index as a Signal, and the
|
|
2050
|
+
* entry's key (the effective ordinal key when `duplicateKeys` is enabled, otherwise
|
|
2051
|
+
* the raw key) — stable for the lifetime of the mapped entry.
|
|
2027
2052
|
* @param options Optional configuration:
|
|
2028
2053
|
* - `onDestroy`: A callback invoked when a mapped item is removed from the array.
|
|
2029
2054
|
* - `key`: A custom key extractor for identity matching (e.g. `(item) => item.id`)
|
|
2030
2055
|
* when item references change but conceptual identity is preserved.
|
|
2056
|
+
* - `duplicateKeys`: Opt-in policy for handling duplicate keys. When omitted,
|
|
2057
|
+
* behavior is unchanged (duplicates collapse). See {@link DuplicateKeyPolicy}.
|
|
2031
2058
|
* @returns A `Signal<U[]>` containing the mapped array.
|
|
2032
2059
|
*
|
|
2033
2060
|
* @example
|
|
@@ -2062,9 +2089,11 @@ function keyArray(source, mapFn, options = {}) {
|
|
|
2062
2089
|
const tempIndexes = [];
|
|
2063
2090
|
const newIndicesNext = [];
|
|
2064
2091
|
const newIndexesCache = new Array();
|
|
2092
|
+
const dup = options.duplicateKeys;
|
|
2065
2093
|
return computed(() => {
|
|
2066
2094
|
const newItems = sourceSignal() || [];
|
|
2067
2095
|
return untracked(() => {
|
|
2096
|
+
const newKeys = dup ? effectiveKeys(newItems, getKey, dup.report) : null;
|
|
2068
2097
|
let i;
|
|
2069
2098
|
let j;
|
|
2070
2099
|
const newLen = newItems.length;
|
|
@@ -2096,7 +2125,7 @@ function keyArray(source, mapFn, options = {}) {
|
|
|
2096
2125
|
items[j] = item;
|
|
2097
2126
|
const indexSignal = signal(j, ...(ngDevMode ? [{ debugName: "indexSignal" }] : []));
|
|
2098
2127
|
newIndexes[j] = indexSignal;
|
|
2099
|
-
newMapped[j] = mapFn(item, indexSignal);
|
|
2128
|
+
newMapped[j] = mapFn(item, indexSignal, newKeys ? newKeys[j] : getKey(item));
|
|
2100
2129
|
}
|
|
2101
2130
|
}
|
|
2102
2131
|
else {
|
|
@@ -2104,27 +2133,30 @@ function keyArray(source, mapFn, options = {}) {
|
|
|
2104
2133
|
temp.length = 0;
|
|
2105
2134
|
tempIndexes.length = 0;
|
|
2106
2135
|
newIndicesNext.length = 0;
|
|
2107
|
-
|
|
2136
|
+
const oldKeys = dup ? effectiveKeys(items, getKey) : null;
|
|
2137
|
+
const kNew = newKeys
|
|
2138
|
+
? (idx) => newKeys[idx]
|
|
2139
|
+
: (idx) => getKey(newItems[idx]);
|
|
2140
|
+
const kOld = oldKeys
|
|
2141
|
+
? (idx) => oldKeys[idx]
|
|
2142
|
+
: (idx) => getKey(items[idx]);
|
|
2143
|
+
for (start = 0, end = Math.min(len, newLen); start < end && kOld(start) === kNew(start); start++) {
|
|
2108
2144
|
newMapped[start] = mapped[start];
|
|
2109
2145
|
newIndexes[start] = indexes[start];
|
|
2110
2146
|
}
|
|
2111
|
-
for (end = len - 1, newEnd = newLen - 1; end >= start &&
|
|
2112
|
-
newEnd >= start &&
|
|
2113
|
-
getKey(items[end]) === getKey(newItems[newEnd]); end--, newEnd--) {
|
|
2147
|
+
for (end = len - 1, newEnd = newLen - 1; end >= start && newEnd >= start && kOld(end) === kNew(newEnd); end--, newEnd--) {
|
|
2114
2148
|
temp[newEnd] = mapped[end];
|
|
2115
2149
|
tempIndexes[newEnd] = indexes[end];
|
|
2116
2150
|
}
|
|
2117
2151
|
for (j = newEnd; j >= start; j--) {
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
i = newIndices.get(key);
|
|
2152
|
+
key = kNew(j);
|
|
2153
|
+
i = newIndices.get(key) ?? -1;
|
|
2121
2154
|
newIndicesNext[j] = i === undefined ? -1 : i;
|
|
2122
2155
|
newIndices.set(key, j);
|
|
2123
2156
|
}
|
|
2124
2157
|
for (i = start; i <= end; i++) {
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
j = newIndices.get(key);
|
|
2158
|
+
key = kOld(i);
|
|
2159
|
+
j = newIndices.get(key) ?? -1;
|
|
2128
2160
|
if (j !== undefined && j !== -1) {
|
|
2129
2161
|
temp[j] = mapped[i];
|
|
2130
2162
|
tempIndexes[j] = indexes[i];
|
|
@@ -2146,7 +2178,7 @@ function keyArray(source, mapFn, options = {}) {
|
|
|
2146
2178
|
else {
|
|
2147
2179
|
const indexSignal = signal(j, ...(ngDevMode ? [{ debugName: "indexSignal" }] : []));
|
|
2148
2180
|
newIndexes[j] = indexSignal;
|
|
2149
|
-
newMapped[j] = mapFn(newItems[j], indexSignal);
|
|
2181
|
+
newMapped[j] = mapFn(newItems[j], indexSignal, kNew(j));
|
|
2150
2182
|
}
|
|
2151
2183
|
}
|
|
2152
2184
|
items.length = newLen;
|