refactor: move type matching to own function
Signed-off-by: Daniel Kesselberg <mail@danielkesselberg.de>
This commit is contained in:
parent
106bd3bf5c
commit
1910947e55
|
@ -55,6 +55,7 @@ import PropertyText from '../Properties/PropertyText.vue'
|
|||
import PropertyMultipleText from '../Properties/PropertyMultipleText.vue'
|
||||
import PropertyDateTime from '../Properties/PropertyDateTime.vue'
|
||||
import PropertySelect from '../Properties/PropertySelect.vue'
|
||||
import { matchTypes } from '../../utils/matchTypes.ts'
|
||||
|
||||
export default {
|
||||
name: 'ContactDetailsProperty',
|
||||
|
@ -246,29 +247,10 @@ export default {
|
|||
// we only use uppercase strings
|
||||
.map(str => str.toUpperCase())
|
||||
|
||||
// Compare array and score them by how many matches they have to the selected type
|
||||
// sorting directly is cleaner but slower
|
||||
// https://jsperf.com/array-map-and-intersection-perf
|
||||
const matchingTypes = this.propModel.options
|
||||
.map(type => {
|
||||
let score = 0
|
||||
const types = type.id.split(',') // "WORK,HOME" => ['WORK', 'HOME']
|
||||
|
||||
if (types.length === selectedType.length) {
|
||||
// additional point for same length
|
||||
score++
|
||||
}
|
||||
|
||||
const intersection = types.filter(value => selectedType.includes(value))
|
||||
score = score + intersection.length
|
||||
|
||||
return { type, score }
|
||||
})
|
||||
|
||||
// Sort by score, filtering out the null score and selecting the first match
|
||||
const matchingType = matchingTypes
|
||||
.sort((a, b) => b.score - a.score)
|
||||
.filter(type => type.score > 0)[0]
|
||||
const matchingType = matchTypes(
|
||||
selectedType,
|
||||
this.propModel.options,
|
||||
)
|
||||
|
||||
if (matchingType) {
|
||||
return matchingType.type
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/**
|
||||
* @copyright Copyright (c) 2023 Daniel Kesselberg <mail@danielkesselberg.de>
|
||||
*
|
||||
* @author Daniel Kesselberg <mail@danielkesselberg.de>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Match a list of types against the available types
|
||||
*
|
||||
* @param {Array<string>} selectedTypes
|
||||
* @param {Array<{id: string, name: string}>} options
|
||||
*/
|
||||
export declare function matchTypes(selectedTypes: Array<string>, options: Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
}>): {
|
||||
type: {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
score: number;
|
||||
} | undefined;
|
|
@ -0,0 +1,50 @@
|
|||
/**
|
||||
* @copyright Copyright (c) 2023 Daniel Kesselberg <mail@danielkesselberg.de>
|
||||
*
|
||||
* @author Daniel Kesselberg <mail@danielkesselberg.de>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Match a list of types against the available types
|
||||
*
|
||||
* @param {Array<string>} selectedTypes
|
||||
* @param {Array<{id: string, name: string}>} options
|
||||
*/
|
||||
export function matchTypes(selectedTypes: Array<string>, options: Array<{id: string, name: string}>) {
|
||||
const items = options.map(option => {
|
||||
let score = 0
|
||||
const types = option.id.split(',') // "WORK,HOME" => ['WORK', 'HOME']
|
||||
|
||||
const intersection = types.filter(value => selectedTypes.includes(value))
|
||||
score = score + intersection.length
|
||||
|
||||
if (selectedTypes.length === types.length && selectedTypes.length === intersection.length) {
|
||||
score++
|
||||
}
|
||||
|
||||
return {
|
||||
type: option,
|
||||
score,
|
||||
}
|
||||
})
|
||||
|
||||
return items
|
||||
.filter(value => value.score > 0)
|
||||
.sort((a, b) => b.score - a.score)
|
||||
.shift()
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
/**
|
||||
* @copyright Copyright (c) 2023 Daniel Kesselberg <mail@danielkesselberg.de>
|
||||
*
|
||||
* @author Daniel Kesselberg <mail@danielkesselberg.de>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
import { matchTypes } from '../../../src/utils/matchTypes'
|
||||
import rfcProps from '../../../src/models/rfcProps.js'
|
||||
|
||||
describe('utils/matchTypes test suite', () => {
|
||||
|
||||
describe('impp', () => {
|
||||
it('matches', () => {
|
||||
const selectedTypes = ['XMPP']
|
||||
|
||||
const match = matchTypes(
|
||||
selectedTypes,
|
||||
rfcProps.properties.impp.options,
|
||||
)
|
||||
|
||||
expect(match).toMatchObject({
|
||||
type: { id: 'XMPP', name: 'XMPP' },
|
||||
score: 2,
|
||||
})
|
||||
})
|
||||
|
||||
it('does not match', () => {
|
||||
const selectedTypes = ['TEST']
|
||||
|
||||
const match = matchTypes(
|
||||
selectedTypes,
|
||||
rfcProps.properties.impp.options,
|
||||
)
|
||||
|
||||
expect(match).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe('tel', () => {
|
||||
it('complete match, one type', () => {
|
||||
const selectedTypes = ['VOICE']
|
||||
|
||||
const match = matchTypes(
|
||||
selectedTypes,
|
||||
rfcProps.properties.tel.options,
|
||||
)
|
||||
|
||||
expect(match).toMatchObject({
|
||||
type: { id: 'VOICE', name: 'Voice' },
|
||||
score: 2,
|
||||
})
|
||||
})
|
||||
|
||||
it('complete match, two types', () => {
|
||||
const selectedTypes = ['HOME', 'VOICE']
|
||||
|
||||
const match = matchTypes(
|
||||
selectedTypes,
|
||||
rfcProps.properties.tel.options,
|
||||
)
|
||||
|
||||
expect(match).toMatchObject({
|
||||
type: { id: 'HOME,VOICE', name: 'Home' },
|
||||
score: 3,
|
||||
})
|
||||
})
|
||||
|
||||
it('partial match, two types', () => {
|
||||
const selectedTypes = ['HOME', 'VOICE']
|
||||
|
||||
const options = [
|
||||
{ id: 'HOME,VOICE,TEST', name: 'Home' },
|
||||
{ id: 'HOME,VOICE', name: 'Home' },
|
||||
{ id: 'HOME', name: 'Home' },
|
||||
{ id: 'WORK,VOICE,TEST', name: 'Work' },
|
||||
{ id: 'WORK,VOICE', name: 'Work' },
|
||||
{ id: 'WORK', name: 'Work' },
|
||||
{ id: 'VOICE', name: 'Voice' },
|
||||
{ id: 'TEST', name: 'Test' },
|
||||
]
|
||||
|
||||
const match = matchTypes(
|
||||
selectedTypes,
|
||||
options,
|
||||
)
|
||||
|
||||
expect(match).toMatchObject({
|
||||
type: { id: 'HOME,VOICE', name: 'Home' },
|
||||
score: 3,
|
||||
})
|
||||
})
|
||||
|
||||
it('does not match', () => {
|
||||
const selectedType = ['TEST']
|
||||
|
||||
const match = matchTypes(
|
||||
selectedType,
|
||||
rfcProps.properties.tel.options,
|
||||
)
|
||||
|
||||
expect(match).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe('misc', () => {
|
||||
it('empty list', () => {
|
||||
const selectedType = ['TEST']
|
||||
|
||||
const match = matchTypes(
|
||||
selectedType,
|
||||
[],
|
||||
)
|
||||
|
||||
expect(match).toBeUndefined()
|
||||
})
|
||||
})
|
||||
})
|
Loading…
Reference in New Issue