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

Multidimensional lists - single item bug #837

Closed
jakob-e opened this Issue Jul 3, 2013 · 11 comments

Comments

Projects
None yet
7 participants
@jakob-e
Copy link

jakob-e commented Jul 3, 2013

"Lists can also have no items in them at all. These lists are represented as ()."
Using this logic one would expect ( ( foo, bar ) ) to be a list with a list as item one
but sadly it isn't. Is this a bug or could someone please explain... otherwise I'll
recommend it as a feature - where ( ) would act the same way as JavaScript [ ].

Best to you all,
Jakob E

// Example:

$list:(
    ('one', 'two', 'three')
); // length 3 - expected length is 1

$list:(
    ('one', 'two', 'three')
    ('four', 'five', 'six')
); // length 2 as expected

$list:(
    ('one', 'two', 'three'),
    ('four', 'five', 'six')
); // length 2 as expected

$list:append( () , ('one', 'two', 'three') ); // length 1 as expected
@chriseppstein

This comment has been minimized.

Copy link
Member

chriseppstein commented Jul 3, 2013

contrary to the impression the syntax for an empty list gives you, the thing that defines a list is not the parens (except when empty). It is the delimiter. So a space and a comma are actually what create lists of values. The parens are completely optional and are simply a grouping mechanism (E.g. 2px, 3px, 4px is a list). Without this (2px * 3px) + (4px / 2px) would not be a standard mathematical construct, it would be addition of two 1-element lists and thus an error.

That said, 1-element lists are a perpetual thorn in our sides and it's annoying that the code required to make a 0, 1, and 2 element lists doesn't have the nice property of being able to simple add and remove list elements and get the expected result.

We've tried to make this work well by adhering to the pattern that any function that takes a list should also assume any value that is not a list, is a list of 1 element, but there are many edge cases where it just doesn't work as expected.

I wish I had a good solution here, but I don't.

@jakob-e

This comment has been minimized.

Copy link

jakob-e commented Jul 3, 2013

Thanks for the sad facts Chris ;)
I'll just have to workaround it - maybe using the :append method

Best,
Jakob E

Update - This seems to work :)

@function list($args...){
    $list:();
    @for $i from 1 through length($args){$list:append($list,nth($args,$i));};
    @return $list;
};

$list1:list((a,b,c)); 
$list2:list(a,b,c);
$list3:list((a,b,c),(d,e,f));
$list4:list(a,b,c,(d,e,f));

.list1 { list:$list1; length:length($list1); first:nth($list1,1); last:nth($list1,length($list1));}
.list2 { list:$list2; length:length($list2); first:nth($list2,1); last:nth($list2,length($list2));}
.list3 { list:$list3; length:length($list3); first:nth($list3,1); last:nth($list3,length($list3));}
.list4 { list:$list4; length:length($list4); first:nth($list4,1); last:nth($list4,length($list4));}

// Output 
.list1 { list: a, b, c; length: 1; first: a, b, c; last: a, b, c; }
.list2 { list: a b c; length: 3; first: a; last: c; }
.list3 { list: a, b, c d, e, f; length: 2; first: a, b, c; last: d, e, f; }
.list4 { list: a b c d, e, f; length: 4; first: a; last: d, e, f; }


// A more complex example
$list5:list((a,b,c),(d,(e1,e2,(e3a,e3b,wally)),f));
$list6:list(list(a,b,c),list(d,list(e1,e2,list(e3a,e3b,wally)),f));

.list5 { list:$list5; length:length($list5);}
.list6 { list:$list6; length:length($list5);}

// Output - Lists looks different
.list5 { list: a, b, c d, e1, e2, e3a, e3b, wally, f; length: 2; }
.list6 { list: a b c d e1 e2 e3a e3b wally f; length: 2; }

// .... but a search for a specific item will return the same
@function listItem($list,$index...){
    $item:$list;
    @for $i from 1 through length($index){$item:nth($item,nth($index,$i));};
    @return $item;
}

.findListItem1 { where-is:listItem($list5,2,2,3,3)}
.findListItem2 { where-is:listItem($list6,2,2,3,3)}

// Output 
.findListItem1 { where-is: wally; }
.findListItem2 { where-is: wally; }

Please do comment :)

@lunelson

This comment has been minimized.

Copy link

lunelson commented Jul 4, 2013

The following separator function which I added through compass, can also be helpful in inspecting lists in order to detect and handle this kind of thing; I believe it will be a built-in feature in 3.3

  def separator(list)
    assert_type list, :List
    Sass::Script::String.new(list.separator.to_s)
  end
  declare :separator, [:list]

e.g. on your first definition of $list it returns 'comma' but on the last one it returns 'space'. I'm using it in a recursive look up function to detect lists that are double-nested, as that last one is.

@robwierzbowski

This comment has been minimized.

Copy link
Contributor

robwierzbowski commented Jul 5, 2013

I believe it will be a built-in feature in 3.3

Yep, here's the issue: #689.

@HugoGiraudel

This comment has been minimized.

Copy link
Contributor

HugoGiraudel commented Jul 13, 2013

contrary to the impression the syntax for an empty list gives you, the thing that defines a list is not the parens (except when empty). It is the delimiter. So a space and a comma are actually what create lists of values. The parens are completely optional and are simply a grouping mechanism (E.g. 2px, 3px, 4px is a list). Without this (2px * 3px) + (4px / 2px) would not be a standard mathematical construct, it would be addition of two 1-element lists and thus an error.

When nesting at more than 2 levels, I believe braces are getting important. Since we only have two delimiters (space and comma), if we want to have 3 or 4-levels deep lists, we have to rely on braces. Or did I miss something?

Although I've figured these complicated lists are easy to parse (no problem whatsover), it occurred to me they are very complicated if not impossible to create with Sass functions. I think this is because braces are not necessarily taken into account, making very deep nesting impossible.

@robwierzbowski

This comment has been minimized.

Copy link
Contributor

robwierzbowski commented Jul 15, 2013

@HugoGiraudel To expand on @chriseppstein's explanation, the parenthesis don't actually make the list, but like in math they give grouping or precedence. So in a b, c (1 2) d, e f Sass says "1 2 is a group, do that first. What do I do with it? The space makes it a list".

@eevee

This comment has been minimized.

Copy link

eevee commented Aug 14, 2013

Could steal from Python and let a trailing comma force a list:

$one-by-three-list: (1 2 3),;

This looks to be a syntax error at the moment, so it should be unambiguous.

@lunelson

This comment has been minimized.

Copy link

lunelson commented Aug 15, 2013

@HugoGiraudel 's recently blogged slice() function can also be used, to create a function that can define a single-item list containing an arbitrary list of items (variable checks removed) [EDIT: this is essentially the same approach as @jakob-e proposed in the 3rd comment; to wrap up a list of items as a single-item list you have to do it with a function]:

@function slice($list, $start: 1, $end: length($list)) {
    $result: ();           
    @for $i from $start through $end {
      $result: append($result, nth($list, $i));
    }
  @return $result;
}

@function singleton($args...) {
    @return slice(($args, ()),1,1);
}

$single-item-list: singleton(1 2 3 4, 5 6 7);

.test {
    length: length($single-item-list);
    content: $single-item-list;
}
.test {
  length: 1;
  content: 1 2 3 4, 5 6 7;
}
@lunelson

This comment has been minimized.

Copy link

lunelson commented Aug 15, 2013

append() is obviously neater for this as @jakob-e pointed out; results same as above.

@function singleton($args...) {
    @return append((), $args);
}
@ghost

This comment has been minimized.

Copy link

ghost commented Aug 15, 2013

Simply having a singleton function would probably make more sense. Following the pythonic idea of simply adding a separator at the end of the list, that means that (1 ) should also be a list because of the extra space when it really shouldn't.

@nex3

This comment has been minimized.

Copy link
Contributor

nex3 commented Jan 8, 2014

As of 7a0a7c9 you can use trailing commas to indicate singleton lists.

@nex3 nex3 closed this Jan 8, 2014

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