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

Proposal for string.split #3284

Merged
merged 16 commits into from
Aug 29, 2022
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
110 changes: 110 additions & 0 deletions proposal/string-split.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# Add split function to Strings Module: Draft 1

*([Issue](https://github.com/sass/sass/issues/1950))*

This proposal adds `string.split()` to the `sass:string` module.

## Table of Contents

* [Background](#background)
* [Summary](#summary)
* [Semantics](#semantics)
* [`string.split()`](#string.split)

## Background

> This section is non-normative.

The `sass:string` module contains several functions for
dvdherron marked this conversation as resolved.
Show resolved Hide resolved
manipulating and finding out information about `strings`.
Currently, though, there is no built-in function that splits
one `string` into a list of substrings, and authors have been
creating their own versions of functions that achieve this
functionality.

## Summary

> This section is non-normative.

This proposal adds the `string.split()` function to the
`sass:string` module. The function takes a `string`, splits it
based on a provided `separator`, and returns a space-separated
list of substrings.

This could be used to take a provided `string` and repurpose
parts of it for some other use. For example, fonts
contained in a font stack list could be split off and
then used as keys in a new map.

Examples:

- ```scss
$fonts: "Helvetica Neue, Helvetica, Arial";
string.split($fonts, ', '); // "Helvetica Neue" "Helvetica" "Arial"
```

A third argument can limit the number of `strings`
returned in the list:

- ```scss
string.split($fonts, ', ', 2); // "Helvetica Neue" "Helvetica"
Copy link
Contributor

Choose a reason for hiding this comment

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

should the limit truncate the end of the string, or should it take all the remainder in the last part ? Javascript is one of the few languages implementing the truncation (which is easy to achieve otherwise by doing an unlimited split and slicing the array)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@stof Are you saying, should this work by 1) making all the splits and then slicing the array at the limit or 2) only splitting up to the limit and then stopping?

Do you think there would be downsides to taking one approach or the other?

Copy link
Contributor

Choose a reason for hiding this comment

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

Well, to me, it would be more useful if limit would work by splitting the string on the limit -1 first separators, with the last item containing all the remaining work.
Your current proposal does the opposite (truncating the remaining parts), and this could be achieved through an unlimited split and then slicing the list (but I just figured out that list.slice does not exist in Sass).

I remember reading an article a few weeks ago comparing the behavior of the string splitting API of lots of different languages in that regard (but unfortunately, I did not manage to find it back for now to share it here. But AFAIR, most languages are providing the non-truncating version of the API (Javascript being one of the exceptions).

Also, a benefit of the non-truncation is that we don't loose content when splitting the string, so joining the list items with the separator (not exposed as a function in sass:list either btw) always produces the original string.

Copy link
Member

Choose a reason for hiding this comment

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

I'd lean towards matching the JS behavior here. @nex3: Do you have any thoughts on this?

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree with @stof here. I think JS's behavior is a misfeature, and we should match other languages in preserving the entire remainder of the string.

Copy link
Member

Choose a reason for hiding this comment

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

In that case, let's change this to do that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jathak I've updated this section (and the examples) to show limit working as noted in the conversation above. The function should preserve the remainder of the string now.

@mirisuzanne made a function based on these steps that you can see here

```


An empty `$separator` returns all characters in the original `string`:
dvdherron marked this conversation as resolved.
Show resolved Hide resolved

- ```scss
$font: "Helvetica"
string.split($font, ''); // "H" "e" "l" "v" "e" "t" "i" "c" "a"
```


## Semantics

This proposal adds the ability to split a `string` to the
dvdherron marked this conversation as resolved.
Show resolved Hide resolved
`sass:string` module.

### `string.split()`
dvdherron marked this conversation as resolved.
Show resolved Hide resolved

```
split($string, $separator, $limit)
dvdherron marked this conversation as resolved.
Show resolved Hide resolved
```

* If `$string` is not a `string`, throw an error.
dvdherron marked this conversation as resolved.
Show resolved Hide resolved

* If `$string` is an empty `string`(`''`), or $separator` is `null`,
dvdherron marked this conversation as resolved.
Show resolved Hide resolved
return a `list` with `$string` as the only item.

* If `$limit` is 0 or `null`, return an empty `list`.
dvdherron marked this conversation as resolved.
Show resolved Hide resolved

* If `$separator` is empty (`''`), return a `list` of each individual
character in `$string`.

* If `$limit` is a value other than an `integer` or `null`, return an
dvdherron marked this conversation as resolved.
Show resolved Hide resolved
empty list.

* Let `split-list` be an empty `list`.

* Let `length` be the value of calling `string.length($string)`.

* Let `index` be the value of calling `string.index($string, $separator)`.

* If `$limit == > 0`:
dvdherron marked this conversation as resolved.
Show resolved Hide resolved

* Let `limit` be the value of `$limit`.

* Otherwise, let `limit` be the value of `string.length($string)`.

* While `list.length(split-list)` is less than `$limit`:

* Call `string.index($string, $separator)` to find the first
dvdherron marked this conversation as resolved.
Show resolved Hide resolved
instance of the `$separator`.

* Let `current-substring` be the returned value of calling
`string.slice($string, 1, `index` - 1)`.
dvdherron marked this conversation as resolved.
Show resolved Hide resolved

* Append `current-substring` to the end of `split-list`.

* Set `$string` to `string.slice($string, index + string.length($separator)`.
dvdherron marked this conversation as resolved.
Show resolved Hide resolved

* Return `split-list`.