Skip to content

Allow specifying collator options (case sensitivity etc.) for “in” expressions #9339

Open
@1ec5

Description

It should be possible for an expression to test whether a string appears in an array or another string while ignoring case or diacritic distinctions, or while following the collation rules of a language other than the current language.

Problem

#6270 added an optional third argument to expression operators such as == and < to override the default collator when comparing two strings, for example to compare the strings case-insensitively or diacritic-insensitively. #8876 implements an in expression operator that streamlines comparing a string against a large or indefinite number of other strings. Unfortunately, this operator takes only two arguments and doesn’t accept a collator, so the developer has to choose between comparing case-insensitively or easily comparing multiple strings.

This issue is surfacing when porting in to NSExpression for iOS/macOS (mapbox/mapbox-gl-native-ios#168), because NSExpression supports [c] and [d] modifiers on the IN operator just as on the == and < operators.

Proposed design

The in expression operator should accept a collator object as an optional third argument:

["in", needle: (boolean, string or number), haystack: (array or string)]: boolean
["in", needle: (boolean, string or number), haystack: (array or string), collator]: boolean

The same issue affects the match operator, but it’s less clear where a collator object would go in a match expression without creating ambiguity for the expression evaluator.

Implementation notes

Here’s where the in comparisons currently take place:

return haystack.indexOf(needle) >= 0;

Here’s how custom collators are evaluated in == expressions:

export const Equals = makeComparison('==', eq, eqCollate);
function makeComparison(op: ComparisonOperator, compareBasic, compareWithCollator) {
return this.collator ?
compareWithCollator(ctx, lhs, rhs, this.collator.evaluate(ctx)) :
compareBasic(ctx, lhs, rhs);
function eqCollate(ctx, a, b, c) { return c.compare(a, b) === 0; }
compare(lhs: string, rhs: string): number {
return this.collator.compare(lhs, rhs);
}
this.collator = new Intl.Collator(this.locale ? this.locale : [],
{sensitivity: this.sensitivity, usage: 'search'});

/ref #6484 mapbox/mapbox-gl-native#11786
/cc @mapbox/gl-js @chloekraw @fabian-guerra

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions