Skip to content

Commit ef82a26

Browse files
authored
fix(shared): correctly compare Map and Set values (#15328)
close #15320
1 parent 6eaecc1 commit ef82a26

3 files changed

Lines changed: 115 additions & 1 deletion

File tree

packages/runtime-dom/__tests__/directives/vModel.spec.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1874,4 +1874,32 @@ describe('vModel', () => {
18741874
await nextTick()
18751875
expect(foo.checked).toEqual(false)
18761876
})
1877+
1878+
// #15320
1879+
it('selects the matching option when values are maps', () => {
1880+
const mapA = new Map([['id', 1]])
1881+
const mapB = new Map([['id', 2]])
1882+
const value = ref(mapB)
1883+
const component = defineComponent(
1884+
() => () =>
1885+
withVModel(
1886+
h(
1887+
'select',
1888+
{
1889+
'onUpdate:modelValue': (val: Map<string, number>) =>
1890+
(value.value = val),
1891+
},
1892+
[
1893+
h('option', { value: mapA }, 'A'),
1894+
h('option', { value: mapB }, 'B'),
1895+
],
1896+
),
1897+
value.value,
1898+
),
1899+
)
1900+
1901+
render(h(component), root)
1902+
1903+
expect(root.querySelector('select').selectedIndex).toBe(1)
1904+
})
18771905
})

packages/shared/__tests__/looseEqual.spec.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,4 +233,59 @@ describe('utils/looseEqual', () => {
233233
expect(looseEqual(arr1, arr5)).toBe(false)
234234
expect(looseEqual(arr5, arr1)).toBe(false)
235235
})
236+
237+
test('compares maps correctly', () => {
238+
const map = new Map<any, any>([
239+
[{ id: 1 }, { value: '1' }],
240+
['foo', 'bar'],
241+
])
242+
243+
expect(looseEqual(map, map)).toBe(true)
244+
expect(
245+
looseEqual(
246+
map,
247+
new Map<any, any>([
248+
['foo', 'bar'],
249+
[{ id: '1' }, { value: 1 }],
250+
]),
251+
),
252+
).toBe(true)
253+
expect(looseEqual(map, new Map([['foo', 'bar']]))).toBe(false)
254+
expect(looseEqual(new Map([['a', 1]]), new Map([['b', 2]]))).toBe(false)
255+
expect(
256+
looseEqual(
257+
new Map<any, any>([
258+
['a', 1],
259+
['b', 2],
260+
]),
261+
new Map<any, any>([
262+
['a', 2],
263+
['b', 1],
264+
]),
265+
),
266+
).toBe(false)
267+
expect(
268+
looseEqual(
269+
new Map<any, any>([
270+
[1, 'value'],
271+
['1', 'value'],
272+
]),
273+
new Map<any, any>([
274+
[1, 'value'],
275+
[2, 'value'],
276+
]),
277+
),
278+
).toBe(false)
279+
})
280+
281+
test('compares sets correctly', () => {
282+
const set = new Set<any>([{ id: 1 }, 'foo'])
283+
284+
expect(looseEqual(set, set)).toBe(true)
285+
expect(looseEqual(set, new Set(['foo', { id: '1' }]))).toBe(true)
286+
expect(looseEqual(set, new Set(['foo']))).toBe(false)
287+
expect(looseEqual(new Set([1]), new Set([2]))).toBe(false)
288+
expect(looseEqual(new Set<any>([1, '1']), new Set<any>([1, 2]))).toBe(false)
289+
expect(looseEqual(new Set(), new Map())).toBe(false)
290+
})
236291
})

packages/shared/src/looseEqual.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { isArray, isDate, isObject, isSymbol } from './general'
1+
import { isArray, isDate, isMap, isObject, isSet, isSymbol } from './general'
22

33
function looseCompareArrays(a: any[], b: any[]) {
44
if (a.length !== b.length) return false
@@ -9,6 +9,27 @@ function looseCompareArrays(a: any[], b: any[]) {
99
return equal
1010
}
1111

12+
function looseCompareCollections(
13+
a: Map<any, any> | Set<any>,
14+
b: Map<any, any> | Set<any>,
15+
) {
16+
if (a.size !== b.size) return false
17+
const candidates = Array.from(b)
18+
const matched = new Uint8Array(candidates.length)
19+
for (const item of a) {
20+
let index = -1
21+
for (let i = 0; i < candidates.length; i++) {
22+
if (!matched[i] && looseEqual(item, candidates[i])) {
23+
index = i
24+
break
25+
}
26+
}
27+
if (index < 0) return false
28+
matched[index] = 1
29+
}
30+
return true
31+
}
32+
1233
export function looseEqual(a: any, b: any): boolean {
1334
if (a === b) return true
1435
let aValidType = isDate(a)
@@ -32,6 +53,16 @@ export function looseEqual(a: any, b: any): boolean {
3253
if (!aValidType || !bValidType) {
3354
return false
3455
}
56+
aValidType = isMap(a)
57+
bValidType = isMap(b)
58+
if (aValidType || bValidType) {
59+
return aValidType && bValidType ? looseCompareCollections(a, b) : false
60+
}
61+
aValidType = isSet(a)
62+
bValidType = isSet(b)
63+
if (aValidType || bValidType) {
64+
return aValidType && bValidType ? looseCompareCollections(a, b) : false
65+
}
3566
const aKeysCount = Object.keys(a).length
3667
const bKeysCount = Object.keys(b).length
3768
if (aKeysCount !== bKeysCount) {

0 commit comments

Comments
 (0)