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

Add the index iteration variable to @each #996

Open
lolmaus opened this Issue Nov 4, 2013 · 13 comments

Comments

Projects
None yet
7 participants
@lolmaus

lolmaus commented Nov 4, 2013

Whenever i do:

@for $i from 1 through length($colors)
  $color: nth($colors, $i)
  .column:nth-child(#{$i})
    background-color: $color

i feel like a kitten dies somewhere in misery and loneliness. And i find myself doing that in almost any project, so that's quite a lot of kittens. :(

I wish i could do this instead:

@each $color, $index in $colors
  .column:nth-child(#{$index})
    background-color: $color

Please spare the kittens. It's our war, not theirs.

@HugoGiraudel

This comment has been minimized.

Show comment
Hide comment
@HugoGiraudel

HugoGiraudel Nov 15, 2013

Contributor

Would be nice, indeed.

Contributor

HugoGiraudel commented Nov 15, 2013

Would be nice, indeed.

@nex3

This comment has been minimized.

Show comment
Hide comment
@nex3

nex3 Dec 6, 2013

Contributor

The suggested syntax steps on the toes of the map/pair iteration in 3.3, so that's not workable.

I'm not entirely opposed to this if a very elegant syntax can be devised, but I'm skeptical of that. The syntax needs to be easy to understand when reading the code for someone with only passing familiarity with Sass, and easy to remember when writing Sass. It should also not give users the impression that constructs like @each can have a million different configurations that do different things. I think this is a tall order, but you're welcome to try.

In the meantime, I don't think it's too burdensome to use a @for loop here. Many languages more powerful than Sass make do with that.

Contributor

nex3 commented Dec 6, 2013

The suggested syntax steps on the toes of the map/pair iteration in 3.3, so that's not workable.

I'm not entirely opposed to this if a very elegant syntax can be devised, but I'm skeptical of that. The syntax needs to be easy to understand when reading the code for someone with only passing familiarity with Sass, and easy to remember when writing Sass. It should also not give users the impression that constructs like @each can have a million different configurations that do different things. I think this is a tall order, but you're welcome to try.

In the meantime, I don't think it's too burdensome to use a @for loop here. Many languages more powerful than Sass make do with that.

@Snugug

This comment has been minimized.

Show comment
Hide comment
@Snugug

Snugug Dec 6, 2013

Contributor

@nex3 what about something like the following:

@each $color in $colors with index $i
Contributor

Snugug commented Dec 6, 2013

@nex3 what about something like the following:

@each $color in $colors with index $i
@lolmaus

This comment has been minimized.

Show comment
Hide comment
@lolmaus

lolmaus Dec 7, 2013

Or something like

@each $color as $index in $colors

lolmaus commented Dec 7, 2013

Or something like

@each $color as $index in $colors
@nex3

This comment has been minimized.

Show comment
Hide comment
@nex3

nex3 Dec 14, 2013

Contributor

@each $color in $colors with index $i

This is okay, although it relies on users knowing the term "index". I'm also not a big fan of requiring two words; I think that contributes to the "word salad" feeling about @each.

@each $color as $index in $colors

as isn't descriptive enough.

Contributor

nex3 commented Dec 14, 2013

@each $color in $colors with index $i

This is okay, although it relies on users knowing the term "index". I'm also not a big fan of requiring two words; I think that contributes to the "word salad" feeling about @each.

@each $color as $index in $colors

as isn't descriptive enough.

@Snugug

This comment has been minimized.

Show comment
Hide comment
@Snugug

Snugug Dec 14, 2013

Contributor

IMO requiring people to know what "index" means when dealing with iterators is OK, but maybe "counter" would work too (although I bet most people who would use this feature would prefer "index")

How about something like this:

@each $color in $colors, index $i (or w/o the ,)

  • or -

@each $color in $colors, counter $i

Contributor

Snugug commented Dec 14, 2013

IMO requiring people to know what "index" means when dealing with iterators is OK, but maybe "counter" would work too (although I bet most people who would use this feature would prefer "index")

How about something like this:

@each $color in $colors, index $i (or w/o the ,)

  • or -

@each $color in $colors, counter $i

@nex3

This comment has been minimized.

Show comment
Hide comment
@nex3

nex3 Dec 16, 2013

Contributor

IMO requiring people to know what "index" means when dealing with iterators is OK, but maybe "counter" would work too (although I bet most people who would use this feature would prefer "index")

It's fine to require knowledge for people who are writing the code, but a major part of the weight of a new syntax construct is the added difficulty of reading the code, especially for inexperienced users or users who aren't well-versed in programming. Manually tracking a counter may be slightly more work when writing the loop in the first place, but it's easy for even an inexperienced reader to figure out what's going on when reading it.

Many widely-used languages have no more concise way of doing this than Sass does right now, and they do just fine. This is why I'm being so stringent in my requirements for this syntax: I think it adds relatively little value and expressiveness, so it needs to be extremely comprehensible to all users in order to mitigate its added weight.

Contributor

nex3 commented Dec 16, 2013

IMO requiring people to know what "index" means when dealing with iterators is OK, but maybe "counter" would work too (although I bet most people who would use this feature would prefer "index")

It's fine to require knowledge for people who are writing the code, but a major part of the weight of a new syntax construct is the added difficulty of reading the code, especially for inexperienced users or users who aren't well-versed in programming. Manually tracking a counter may be slightly more work when writing the loop in the first place, but it's easy for even an inexperienced reader to figure out what's going on when reading it.

Many widely-used languages have no more concise way of doing this than Sass does right now, and they do just fine. This is why I'm being so stringent in my requirements for this syntax: I think it adds relatively little value and expressiveness, so it needs to be extremely comprehensible to all users in order to mitigate its added weight.

@Snugug

This comment has been minimized.

Show comment
Hide comment
@Snugug

Snugug Dec 17, 2013

Contributor

Yah, I understand that's a concern. I agree that it's entirely possible for this functionality to be done in round-about ways currently and that many languages don't have an each with iterator, so it should be OK for Sass.

I think the key distinction, though, between Sass and other languages in terms of an each with iterator is the medium in which we're working. In Sass, because we're outputting to CSS, there is a need for both strings and numbers coming from an each, especially CSS's nth. Frameworks in Sass often rely upon lots of lists (and now maps), and those often need indexes when looping. I'm sure, of course, you understand all of this, so I'm not going to continue.

Instead, let's discuss syntax. What are your current objections to counter $i or index $i?

Contributor

Snugug commented Dec 17, 2013

Yah, I understand that's a concern. I agree that it's entirely possible for this functionality to be done in round-about ways currently and that many languages don't have an each with iterator, so it should be OK for Sass.

I think the key distinction, though, between Sass and other languages in terms of an each with iterator is the medium in which we're working. In Sass, because we're outputting to CSS, there is a need for both strings and numbers coming from an each, especially CSS's nth. Frameworks in Sass often rely upon lots of lists (and now maps), and those often need indexes when looping. I'm sure, of course, you understand all of this, so I'm not going to continue.

Instead, let's discuss syntax. What are your current objections to counter $i or index $i?

@Snugug

This comment has been minimized.

Show comment
Hide comment
@Snugug

Snugug Dec 17, 2013

Contributor

One place where I think this can make it much easier to work is an each with key/value. Take the following (which is what we need to do now):

$zoo: (
  "salamander": (
    background: green,
    border-color: red
  ),
  "chicken": (
    background: yellow,
    border-color: red
  ),
  "elephant": (
    background: purple,
    border-color: blue
  )
);

$zoo-length: length($zoo);

@for $i from 1 through $zoo-length {
  $animal: nth($zoo, $i);
  $name: nth($animal, 1);
  $properties: nth($animal, 2);
  li:nth-of-type(#{$i}) {
    content: $name;
    @each $key, $value in $properties {
      #{$key}: $value;
    }
  }
}

This could be rewritten in the following way (which IMO is much more legible). While a user may not know what an index is immediately, what the code is trying to do is fairly easy to comprehend (much more so than the original)

$zoo: (
  "salamander": (
    background: green,
    border-color: red
  ),
  "chicken": (
    background: yellow,
    border-color: red
  ),
  "elephant": (
    background: purple,
    border-color: blue
  )
);

@each $animal, $properties in $zoo, counter $i {
  li:nth-of-type(#{$i}) {
    content: $name;
    @each $property, feature in $properties {
      #{$key}: $value;
    }
  }
}
Contributor

Snugug commented Dec 17, 2013

One place where I think this can make it much easier to work is an each with key/value. Take the following (which is what we need to do now):

$zoo: (
  "salamander": (
    background: green,
    border-color: red
  ),
  "chicken": (
    background: yellow,
    border-color: red
  ),
  "elephant": (
    background: purple,
    border-color: blue
  )
);

$zoo-length: length($zoo);

@for $i from 1 through $zoo-length {
  $animal: nth($zoo, $i);
  $name: nth($animal, 1);
  $properties: nth($animal, 2);
  li:nth-of-type(#{$i}) {
    content: $name;
    @each $key, $value in $properties {
      #{$key}: $value;
    }
  }
}

This could be rewritten in the following way (which IMO is much more legible). While a user may not know what an index is immediately, what the code is trying to do is fairly easy to comprehend (much more so than the original)

$zoo: (
  "salamander": (
    background: green,
    border-color: red
  ),
  "chicken": (
    background: yellow,
    border-color: red
  ),
  "elephant": (
    background: purple,
    border-color: blue
  )
);

@each $animal, $properties in $zoo, counter $i {
  li:nth-of-type(#{$i}) {
    content: $name;
    @each $property, feature in $properties {
      #{$key}: $value;
    }
  }
}
@lolmaus

This comment has been minimized.

Show comment
Hide comment
@lolmaus

lolmaus Jan 10, 2014

I got curious how Python folk resolve this issue, as Python doesn't boast to be syntactically sweet as well as Sass.

It turned out, they use an enumerate helper function for this which does a simple job: turns an array into a dictionary.

So i made a similar function for Sass.

Usage:

$list: (dog, cat, bird, cow)

@each $index, $value in enumerate($list)
  li:nth-child( #{ length($list) }n + #{ $index } ):before
    content: quote($value)

Source:

@function enumerate($list)
  $map: ()

  @for $index from 1 through  length($list)
    $value: nth($list, $index)
    $map: map-merge($map, ($index: $value))

  @return $map

Seems to be working fine and handy to use.

Demo: http://sassmeister.com/gist/8346425

It doesn't cover @Snugug's nifty use case though.

lolmaus commented Jan 10, 2014

I got curious how Python folk resolve this issue, as Python doesn't boast to be syntactically sweet as well as Sass.

It turned out, they use an enumerate helper function for this which does a simple job: turns an array into a dictionary.

So i made a similar function for Sass.

Usage:

$list: (dog, cat, bird, cow)

@each $index, $value in enumerate($list)
  li:nth-child( #{ length($list) }n + #{ $index } ):before
    content: quote($value)

Source:

@function enumerate($list)
  $map: ()

  @for $index from 1 through  length($list)
    $value: nth($list, $index)
    $map: map-merge($map, ($index: $value))

  @return $map

Seems to be working fine and handy to use.

Demo: http://sassmeister.com/gist/8346425

It doesn't cover @Snugug's nifty use case though.

@chriseppstein

This comment has been minimized.

Show comment
Hide comment
@chriseppstein

chriseppstein Feb 24, 2014

Member

The above code is how I'd like to address this. It can be made more efficient by adding a Sass function like range($n). So that range(10) => (1, 2, 3, 4, 5, 6, 7, 8, 9, 10). Then enumerate can become:

@function enumerate($list-or-map) {
  @return zip($list-or-map, range(length($list-or-map));
}
Member

chriseppstein commented Feb 24, 2014

The above code is how I'd like to address this. It can be made more efficient by adding a Sass function like range($n). So that range(10) => (1, 2, 3, 4, 5, 6, 7, 8, 9, 10). Then enumerate can become:

@function enumerate($list-or-map) {
  @return zip($list-or-map, range(length($list-or-map));
}
@yairEO

This comment was marked as spam.

Show comment
Hide comment
@yairEO

yairEO Feb 3, 2016

is this still relevant ?

update: it's November 2016, whats up? I'm practically sitting and waiting for this :/

yairEO commented Feb 3, 2016

is this still relevant ?

update: it's November 2016, whats up? I'm practically sitting and waiting for this :/

@bstrilziw

This comment was marked as spam.

Show comment
Hide comment
@bstrilziw

bstrilziw Apr 19, 2017

I really would love an index interator variable, too. :)

bstrilziw commented Apr 19, 2017

I really would love an index interator variable, too. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment