Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve sorting candidates containing numbers #13507

Merged
merged 10 commits into from Apr 15, 2024

Conversation

RobinMalfait
Copy link
Contributor

@RobinMalfait RobinMalfait commented Apr 11, 2024

This PR adds a custom compare function that compares two strings. However, once a number is reached the numbers are compared as actual numbers instead of their string representation.

Before this change, p-1 p-2 p-10 p-20 would be sorted as p-1 p-10 p-2 p-20. After this change, the order is p-1 p-2 p-10 p-20.

This makes the suggestions in the vscode extension more logical.

The implementation compares two strings, and as long as the strings don't contain any numbers, it compares them as strings. This way we don't pay a performance penalty for comparing strings that don't contain numbers.

Testing this change on the tailwindcss.com codebase where we have to store ~3200 unique classes the time spend on sorting the classes went from ~2.35ms to ~3.29ms~3.10ms. This means that there is about a ~1ms~0.75ms performance penalty for sorting this.

Another thing we could have done is to compare using the localeCompare method:

function compare(a, b) {
  return a.localeCompare(b, undefined, { numeric: true });
}

However, this is an order of magnitude slower than the current implementation, and sorting takes around ~30ms instead.


Outstanding questions:

  1. We could only do this for the VS Code extension and Prettier plugin because then we wouldn't have to pay the performance penalty of ~1ms~0.75ms. This however means that the order in the extension or the Prettier plugin would be different compared to the order in the generated CSS. Should we only do it there or everywhere and pay the ~1ms~0.75ms penalty in favor of nicer consistency? The performance is below ~1ms so good to include everywhere 👍

This `compare` function compares two strings. However, once a number is
reached the numbers are compared as actual numbers instead of the string
representation.

E.g.:

```
p-1
p-2
p-10
p-20
```

Will be sorted as expected in this order, instead of

```
p-1
p-10
p-2
p-20
```

---

This should also make suggestions in the vscode extension more logical.
@RobinMalfait RobinMalfait changed the title Improve sorting candidates with numbers Improve sorting candidates containing numbers Apr 11, 2024
@RobinMalfait RobinMalfait changed the title Improve sorting candidates containing numbers Improve sorting candidates containing numbers Apr 11, 2024
This makes the code more correct _and_ improves performance because the
`Number(…)` will now always deal with numbers.

On the tailwindcss.com codebase, sorting now goes from `~3.29ms` to
`~3.10ms`
In this branch, it's guaranteed that numbers are _different_ which means
that they are never going to be the same thus unreachable code.

When we compare two strings such as:

```
foo-123-bar
foo-123-baz
```

Then all characters until the last character is the same character in
both positions. This means that "numbers" that are the same in the same
position will be compared as strings instead of numbers. But that is
fine because they are the same anyway.
This can happen if we are sorting `0123` and `123`. The `Number`
representation will be equal, but the string is not.

Will rarely or even never happen. But if it does, this makes it
deterministic.
@adamwathan
Copy link
Member

Nice — hard to tell just scanning the code but what's the behavior for strings that have numbers in the middle of them (pt-48-potato)? Or classes with modifiers (text-16/loose)?

@thecrypticace
Copy link
Contributor

Something like text-16/loose vs text-16/wide should get sorted alphabetically because everything up to the / is identical. We should definitely add a test proving this though.

Copy link
Contributor

@thecrypticace thecrypticace left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything looks good from what I can tell, but we should add some more tests for utilities with numbers and modifiers. (Just in the compare tests seems fine to me)

@RobinMalfait
Copy link
Contributor Author

I'll add some more tests cases 👍

adamwathan and others added 2 commits April 13, 2024 05:59
This also gets rid of some explanation that can now be omitted entirely.
@RobinMalfait RobinMalfait merged commit cd0c308 into next Apr 15, 2024
1 check passed
@RobinMalfait RobinMalfait deleted the fix/sort-numbers-in-strings branch April 15, 2024 15:56
Comment on lines +22 to +25
let aStart = i
let aEnd = i
let zStart = i
let zEnd = i
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, aEnd and zEnd could have been i + 1 to prevent comparing the same offset twice.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants