Search code examples
cssarraysruby-on-railsrubycombinations

Adding class to item in array based on it's value


so I simply want to change the colour of the text of a list of arrays based on the word that's in the string.

Here's my array of combinations currently being outputted in the view with <%= @output %>:

[{"Mer lec sai ham"=>{:price=>13.1, :points=>53.4}}, {"Mer rus sai ham"=>{:price=>12.1, :points=>32.2}}, {"Fer rus sai ham"=>{:price=>13.1, :points=>31.4}}, {"Mcl rus sai ham"=>{:price=>13.5, :points=>14.9}}]

CSS:

 span.mer{color:red;}
 span.lec{color:green;}

And then I am just calling it in my view using <%= @combo %>

The ouput should be the list of combinations but this time just the words are coloured correctly. This is currently just showing nothing, it's not throwing errors though.

EDIT Added my full controller code in case it's needed:

# DEFINE VARIABLE AND TARGET
     teams = team_price.keys
     drivers = driver_price.keys
     target = 13.5

    # CREATE METHOD TO SUM BOTH PRICES AND POINTS FOR GIVEN COMBINATION
    def add_up(combo, ht, hd)
      t, d = combo
      ht[t] + hd.values_at(*d).sum
    end

    # METHOD TO DOUBLE HIGHEST DRIVER POINTS
    def add_dbl(combo, team_points, driver_points)
      t, ds = combo
      dmax = ds.max_by { |d| driver_points[d] }
      driver_points[dmax] + add_up(combo, team_points, driver_points)
    end

    # ALL POSSIBLE COMBINATIONS OF TEAM AND DRIVERS
    all_driver_combos = drivers.combination(3).to_a
    all_combos = teams.product(all_driver_combos)

    # SHOW ALL COMBOS WHERE SUM DOES NOT EXCEED TARGET
    valid_combos = all_combos.select do |c|
      add_up(c, team_price, driver_price) <= target
    end

    # SORT VALID COMBOS BY SUM OF POINTS FOR EACH ELEMENT
    ordered = valid_combos.sort_by do |c|
      -add_dbl(c, team_points, driver_points)
    end

    # SORT DRIVERS BY HIGHEST POINTS
    ordered.each do |_t,ds|
      ds.sort_by! { |d| -driver_points[d] }
    end

    # OUTPUT
    output = ordered.map do |c|
      { c.join(" ")=>{ price: add_up(c, team_price, driver_price),
             points: add_dbl(c, team_points, driver_points).round(2)} }
    end

    # CONVERT OUTPUT TO HASH
    @output = output.reduce Hash.new, :merge

Table Edit I would now like to place this combination output in a table as follows (whilst keeping the colour coding solution):

Team Double Drivers Price Points
Mer lec sai ham 13.1 53.4
Mer rus sai ham 12.1 32.2
Fer rus sai ham 13.1 31.4

In my view, I now have:

<table class="table table-condensed">
   <thead>
      <tr>
      <th>Combo</th>
      <th>Price</th>
      <th>Points</th>
      </tr>
   </thead>
   <tbody>
      <% @output.each do |key,value| %>
         <tr>
         <td><%= raw(key.gsub(/Mer/, '<span class="mer">Mer</span>')) %></td>
         <td><%= value[:price] %></td>
         <td><%= value[:points] %></td>
         </tr>
      <% end %>
   </tbody>
</table>

Which is currently outputting:


Combo           Price   Points
Mer lec sai ham 13.1    53.4
Mer rus sai ham 12.1    32.2
Fer rus sai ham 13.1    31.4
Mcl rus sai ham 13.5    14.9

However as shown in the table example above I would like it so that 'Combo' is split up into the correct columns. Like so:

Before: Mer lec sai ham | 13.1 | 53.4

After: Mer | lec | sai ham | 13.1 | 53.4

Price and points work fine, but it's just a case of splitting up the combo. Looking at the table I would like to create, the 'team' (e.g Mer, fer) is always going to be the first value in the array. 'Double' is always the second value in the array (lec in the above case) and then 'Driver' is just the remaining values (sai ham in the above case). Because the array is ordered in this way is there a way to pull each out using something like: .key[1] or somehow assign each key a new name? Thanks for the help!


Solution

  • It looks like you are using assignment (=) when you should be using comparison (==)?

    @combo = @output.each do |p|
      if p = 'Mer'
        class_name = 'red'
      elsif p = 'lec'
        class_name = 'green'
      else
        class_name = 'black'
      end
    end
    

    Instead of

     @combo = @output.each do |p|
          if p == 'Mer'
            class_name = 'red'
          elsif p == 'lec'
            class_name = 'green'
          else
            class_name = 'black'
          end
        end
    

    Also you are passing a hash as p and trying to compare it to a string. You don't need combo at all and should leave interface concerns out of your controller code. I would get rid of @combo. As far as stying things using CSS I try to keep it semantic. So I would name classes like:

    span.mer{color:red;}
    span.lec{color:green;}
    

    And just leave black as the default. Edit Also if you can change your @output to just a simple hash of items with the string as the key and the hash of price and points as the value like this:

    @output = {"Mer lec sai ham"=>{:price=>13.1, :points=>53.4}, "Mer rus sai ham"=>{:price=>12.1, :points=>32.2}, "Fer rus sai ham"=>{:price=>13.1, :points=>31.4}, "Mcl rus sai ham"=>{:price=>13.5, :points=>14.9}}
    

    Possible view code(Edited to address request for table):

    <table class="table table-condensed">
      <thead>
      <tr>
        <th>Team</th>
        <th>Double</th>
        <th>Drivers</th>
        <th>Price</th>
        <th>Points</th>
      </tr>
      </thead>
      <tbody>
      <% @output.each do |key,value| %>
        <tr>
          <% key = key.split(' ') %>
          <% key = "<td>#{key[0]}</td><td>#{key[1]}</td><td>#{key[2]} #{key[3]}</td>" %>
          <%= raw(key.gsub(/Mer/, '<span class="mer">Mer</span>')) %>
          <td><%= value[:price] %></td>
          <td><%= value[:points] %></td>
        </tr>
      <% end %>
      </tbody>
    </table>
    

    This will wrap any word with a <span> tag that applies the color to that word based on the css defs. If "Lec" could appear as "Lec" or "lec" I would change the regexp in gsub to be case insensitive.