Skip to content

Commit

Permalink
#5564 - Add support for searching ambiguous monomers (#5680)
Browse files Browse the repository at this point in the history
  • Loading branch information
mnmsvlw authored Oct 17, 2024
1 parent ffc17e8 commit 946ccdb
Showing 1 changed file with 148 additions and 105 deletions.
253 changes: 148 additions & 105 deletions packages/ketcher-macromolecules/src/state/library/librarySlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
MonomerGroups,
AmbiguousMonomerType,
isAmbiguousMonomerLibraryItem,
IKetIdtAliases,
} from 'ketcher-core';
import {
LibraryNameType,
Expand Down Expand Up @@ -170,9 +171,12 @@ export const getSearchTermValue = (state): string => {
};

export const selectMonomersInCategory = (
items: MonomerItemType[],
items: MonomerOrAmbiguousType[],
category: LibraryNameType,
) => items.filter((item) => item.props?.MonomerType === category);
) =>
items.filter(
(item) => !item.isAmbiguous && item.props?.MonomerType === category,
) as MonomerItemType[];

export const selectAmbiguousMonomersInCategory = (
libraryItems: MonomerOrAmbiguousType[],
Expand Down Expand Up @@ -273,12 +277,13 @@ export const selectAmbiguousMonomersInCategory = (
return groupedAmbiguousMonomerLibraryItems;
};

export const selectUnsplitNucleotides = (items: MonomerItemType[]) =>
export const selectUnsplitNucleotides = (items: MonomerOrAmbiguousType[]) =>
items.filter(
(item) =>
item.props?.MonomerClass === KetMonomerClass.RNA ||
item.props?.MonomerClass === KetMonomerClass.DNA,
);
!item.isAmbiguous &&
(item.props?.MonomerClass === KetMonomerClass.RNA ||
item.props?.MonomerClass === KetMonomerClass.DNA),
) as MonomerItemType[];

export const selectMonomersInFavorites = (items: MonomerOrAmbiguousType[]) =>
items.filter((item) => item.favorite && !item.isAmbiguous);
Expand Down Expand Up @@ -309,133 +314,171 @@ export const selectAmbiguousMonomersInFavorites = (

export const selectFilteredMonomers = createSelector(
(state: RootState) => state.library,
(state): Array<MonomerItemType & { favorite: boolean }> => {
(state): Array<MonomerOrAmbiguousType & { favorite: boolean }> => {
const { searchFilter, monomers, favorites } = state;
const normalizedSearchFilter = searchFilter.toLowerCase();

return monomers
.filter((item: MonomerItemType) => {
const { Name = '', MonomerName = '', idtAliases } = item.props;
const monomerName = Name.toLowerCase();
const monomerNameFull = MonomerName.toLowerCase();
const checkMonomerMatch = (
name = '',
fullName = '',
idtAliases: IKetIdtAliases | undefined,
searchFilter: string,
) => {
const monomerName = name.toLowerCase();
const monomerNameFull = fullName.toLowerCase();

const idtBase = idtAliases?.base?.toLowerCase();
const idtBase = idtAliases?.base?.toLowerCase();

const idtModifications = idtAliases?.modifications
? Object.values(idtAliases.modifications)
.map((mod) => mod.toLowerCase())
.join(' ')
: '';
const idtModifications = idtAliases?.modifications
? Object.values(idtAliases.modifications)
.map((mod) => mod.toLowerCase())
.join(' ')
: '';

if (normalizedSearchFilter === '/') {
return Boolean(idtBase || idtModifications);
}
if (searchFilter === '/') {
return Boolean(idtBase || idtModifications);
}

if (normalizedSearchFilter.includes('/')) {
const parts = normalizedSearchFilter.split('/');
if (searchFilter.includes('/')) {
const parts = searchFilter.split('/');

if (parts.length > 3 || (parts.length === 3 && parts[2] !== '')) {
return false;
}
if (parts.length > 3 || (parts.length === 3 && parts[2] !== '')) {
return false;
}

if (parts.length === 3 && parts[1] !== '') {
const textBetweenSlashes = parts[1];

const matchesIdtBase =
idtBase &&
idtBase.length === textBetweenSlashes.length &&
Array.from(idtBase).every(
(char, index) => char === textBetweenSlashes[index],
);

const matchesIdtModifications = idtModifications
? idtModifications
.split(' ')
.some(
(mod) =>
mod.length === textBetweenSlashes.length &&
Array.from(mod).every(
(char, index) => char === textBetweenSlashes[index],
),
)
: false;

return matchesIdtBase || matchesIdtModifications;
}

const searchBeforeSlash = parts[0];
const searchAfterSlash = parts[1];

if (
normalizedSearchFilter.startsWith('/') &&
normalizedSearchFilter.length > 1
) {
const aliasRest = normalizedSearchFilter.slice(1);
return (
(idtBase && idtBase.startsWith(aliasRest)) ||
(idtModifications &&
idtModifications
.split(' ')
.some((mod) => mod.startsWith(aliasRest)))
);
}

if (
normalizedSearchFilter.endsWith('/') &&
normalizedSearchFilter.length > 1
) {
const aliasRest = normalizedSearchFilter.slice(0, -1);
const aliasLastSymbol =
normalizedSearchFilter[normalizedSearchFilter.length - 2];

return (
(idtBase &&
idtBase.endsWith(aliasRest) &&
idtBase[idtBase.length - 1] === aliasLastSymbol) ||
(idtModifications &&
idtModifications
.split(' ')
.some(
(mod) =>
mod.endsWith(aliasRest) &&
mod[mod.length - 1] === aliasLastSymbol,
))
);
}
if (parts.length === 3 && parts[1] !== '') {
const textBetweenSlashes = parts[1];

const matchesIdtBase =
idtBase &&
idtBase.startsWith(searchAfterSlash) &&
idtBase.endsWith(searchBeforeSlash);
idtBase.length === textBetweenSlashes.length &&
Array.from(idtBase).every(
(char, index) => char === textBetweenSlashes[index],
);

const matchesIdtModifications = idtModifications
? idtModifications
.split(' ')
.some(
(mod) =>
mod.startsWith(searchAfterSlash) &&
mod.endsWith(searchBeforeSlash),
mod.length === textBetweenSlashes.length &&
Array.from(mod).every(
(char, index) => char === textBetweenSlashes[index],
),
)
: false;

return matchesIdtBase || matchesIdtModifications;
}

const matchesIdtBase = idtBase
? idtBase.includes(normalizedSearchFilter)
: false;
const searchBeforeSlash = parts[0];
const searchAfterSlash = parts[1];

if (searchFilter.startsWith('/') && searchFilter.length > 1) {
const aliasRest = searchFilter.slice(1);
return (
(idtBase && idtBase.startsWith(aliasRest)) ||
(idtModifications &&
idtModifications
.split(' ')
.some((mod) => mod.startsWith(aliasRest)))
);
}

if (searchFilter.endsWith('/') && searchFilter.length > 1) {
const aliasRest = searchFilter.slice(0, -1);
const aliasLastSymbol = searchFilter[searchFilter.length - 2];

return (
(idtBase &&
idtBase.endsWith(aliasRest) &&
idtBase[idtBase.length - 1] === aliasLastSymbol) ||
(idtModifications &&
idtModifications
.split(' ')
.some(
(mod) =>
mod.endsWith(aliasRest) &&
mod[mod.length - 1] === aliasLastSymbol,
))
);
}

const matchesIdtBase =
idtBase &&
idtBase.startsWith(searchAfterSlash) &&
idtBase.endsWith(searchBeforeSlash);
const matchesIdtModifications = idtModifications
? idtModifications.includes(normalizedSearchFilter)
? idtModifications
.split(' ')
.some(
(mod) =>
mod.startsWith(searchAfterSlash) &&
mod.endsWith(searchBeforeSlash),
)
: false;

const cond =
monomerName.includes(normalizedSearchFilter) ||
monomerNameFull.includes(normalizedSearchFilter) ||
matchesIdtBase ||
matchesIdtModifications;
return matchesIdtBase || matchesIdtModifications;
}

const matchesIdtBase = idtBase ? idtBase.includes(searchFilter) : false;
const matchesIdtModifications = idtModifications
? idtModifications.includes(searchFilter)
: false;

return cond;
const cond =
monomerName.includes(searchFilter) ||
monomerNameFull.includes(searchFilter) ||
matchesIdtBase ||
matchesIdtModifications;

return cond;
};

return monomers
.filter((item: MonomerOrAmbiguousType) => {
if (item.isAmbiguous) {
const {
label,
id,
idtAliases,
monomers: components,
} = item as AmbiguousMonomerType;

const matchesMonomer = checkMonomerMatch(
label,
id,
idtAliases,
normalizedSearchFilter,
);

return (
matchesMonomer ||
components.some((monomer) => {
const { Name, MonomerName, idtAliases } =
monomer.monomerItem.props;

return checkMonomerMatch(
Name,
MonomerName,
idtAliases,
normalizedSearchFilter,
);
})
);
} else {
const { Name, MonomerName, idtAliases } = (item as MonomerItemType)
.props;

return checkMonomerMatch(
Name,
MonomerName,
idtAliases,
normalizedSearchFilter,
);
}
})
.map((item: MonomerItemType) => {
.map((item: MonomerOrAmbiguousType) => {
return {
...item,
favorite: !!favorites[getMonomerUniqueKey(item)],
Expand Down

0 comments on commit 946ccdb

Please sign in to comment.