ruby-style-guide icon indicating copy to clipboard operation
ruby-style-guide copied to clipboard

Style for Arrays of Hashes and similar constructs

Open michaelmarziani opened this issue 10 years ago • 12 comments

I came to check the guide for suggestions but didn't see anything for this.

# clear but can we make it more compact?
people = [
  {
    :name => "John Smith",
    :address => "123 Street St, City, Region, Country"
  },
  {
    :name => "Jane Smith",
    :address => "321 Avenue Ave, City, Region, Country"
  }
]

# compact but less readable if we start wrapping, likely if we add additional keys
people = [
  { :name => "John Smith", :address => "123 Street St, City, Region, Country" },
  { :name => "Jane Smith", :address => "321 Avenue Ave, City, Region, Country" }
]

# fairly clear but the braces don't line up and we only saved 2 rows
people = [{
    :name => "John Smith",
    :address => "123 Street St, City, Region, Country"
  },
  {
    :name => "Jane Smith",
    :address => "321 Avenue Ave, City, Region, Country"
}]

I'm not totally happy with any of these. Any other suggestions?

Thanks for all the hard work on this guide, BTW!

-Michael

michaelmarziani avatar Mar 13 '14 08:03 michaelmarziani

I usually do 2 for small hashes and 3 for the bigger ones, except I put the closing and opening brackets between the hashes in the same line:

people = [{
    :name => "John Smith",
    :address => "123 Street St, City, Region, Country"
  }, {
    :name => "Jane Smith",
    :address => "321 Avenue Ave, City, Region, Country"
}]

fuadsaud avatar Mar 18 '14 01:03 fuadsaud

I tend to follow @michaelmarziani's example, except I line up my values. I don't worry about the number of lines so much (thank you, text folding in competent editors), but I have been known to pull sizeable declarations out into separate files/objects. (SRP FTW!)

My current convention would look like

people = [{
    :name =>    "John Smith",
    :address => "123 Street St, City, Region, Country"
  },
  {
    :name =>    "Jane Smith",
    :address => "321 Avenue Ave, City, Region, Country"
  }]

or, for code that's been touched post-Ruby 1.9,

people = [{
    name:     "John Smith",
    address:  "123 Street St, City, Region, Country"
  },
  {
    name:     "Jane Smith",
    address:  "321 Avenue Ave, City, Region, Country"
  }]

jdickey avatar Mar 18 '14 04:03 jdickey

@fuadsaud: I like the close and open brackets on one line; it remains readable and saves an extra line per array element. Thanks for that suggestion!

@fuadsaud's answer also raises a related question: Is it better to always follow the same style convention or is it ok to use "style 2" for smaller hashes, and a variant of "style 3" for larger ones? Stated another way, should we aim for consistency whenever we create a structure like this, or should we aim for readability, allowing > 1 style convention so long as the declaration is clear?

michaelmarziani avatar Mar 18 '14 21:03 michaelmarziani

@jdickey I think lining up the values makes for even better readability. Thanks! The only thing unclear in your examples is unequal indentation of the opening and closing of the array:

# unclear that these go together when scanning down code
people = [{
  }]

# vs what I believe is considered best practice
people = [{
}]

michaelmarziani avatar Mar 18 '14 21:03 michaelmarziani

@michaelmarziani I tend to prefer the indented closing brace/bracket to make the next line (which isn't indented) stand out more, as if to say "I'm not part of what came before me!"

I used to prefer the second example you give. Then I did some heavy cut-and-paste rearrangement of some legacy code and found that the indentation makes for easier reading when you're whipping the selection cursor down the page.

jdickey avatar Mar 20 '14 15:03 jdickey

I prefer a third option:

# clear that these go together, and stands out from what comes after
people = [{
           ...
         }]

eclubb avatar Mar 20 '14 16:03 eclubb

@eclubb I tend to dislike that, on the grounds that it penalises descriptive, intention-revealing variable names. If the array's name is more than a half-dozen or so characters long and if the key/value pairs in the hash are longer than 70 or so characters, the lines will wrap if you have a wrap margin enabled at 80 (as other style-guide recommendations would suggest).

By having the keys indented one or at most two two-position levels from the surrounding code, that leaves more room before the right margin.

jdickey avatar Mar 20 '14 16:03 jdickey

You can also break the line after the =

people =
  [{
         ...
  }]

Not that I like it much, but it's possible.

fuadsaud avatar Mar 20 '14 16:03 fuadsaud

Points for effort, @fuadsaud, but I don't like it much, either. :-1:

jdickey avatar Mar 20 '14 16:03 jdickey

@jdickey I see your point. Have you run into those situations often? I have some, but in my experience they're pretty rare. I usually hit them when the value is built from calling methods with longish names on objects with longish names (as opposed to using local variables for the value.

longish_hash_name = {
                      longish_key_name: here_is_an_object.with_a_longish_method_name
                    }

In which case, i might do something like:

local_variable = here_is_an_object.with_a_longish_method_name
longish_hash_name = {
                      longish_key_name: local_variable
                    }

I could, however, see it being more of a problem in deeply nested code. When extracting a local variable won't help, I will usually fall back to one level of indentation:

longish_hash_name = {
  longish_key_name: here_is_an_object.with_a_longish_method_name
}

eclubb avatar Mar 20 '14 16:03 eclubb

another option is to parse raw text, it is limited but looks well for given data https://gist.github.com/sowcow/9679560

sowcow avatar Mar 21 '14 04:03 sowcow

@eclubb I have run into that sort of situation often enough that it's troublesome. Often, but not always, it's an indicator of some overly-complex code. Say, you've got a couple of if or case statements that move the margin in three or four indentation levels from the beginning of a method within a module, so you've chewed up a dozen or so spaces before you've even started — and your shop standards call for 80-column soft-wrapped lines. So your

longish_hash_name = {
  longish_key_name: here_is_an_object.with_a_longish_method_name
  }

example would just fit (64 positions for the key/value pair indented two spaces from the hash name, plus call it a dozen spaces indentation for module/class/method/whatever). And then you decide to be "neat" and have all the values vertically aligned and boom here comes ugly auto-wrapping. Breaking that nesting up into nice, neat modules/methods while keeping the organisation of the code tight enough that people don't get lost is an art that too few have mastered.

And yes, one level of indentation is a Good Thing. (If you look at the examples in my first comment, the keys appear to be indented two levels from the start of the assignment to people:

people = [{
    name:     "John Smith",
    address:  "123 Street St, City, Region, Country"
  },
  {
    name:     "Jane Smith",
    address:  "321 Avenue Ave, City, Region, Country"
  }]

Note, however, that the array items, being hashes, have their opening and closing braces indented one level from person, with the keys one level beyond that. A detail that I'm usually willing to yield to a sufficiently truculent colleague.

jdickey avatar Mar 22 '14 18:03 jdickey