Search code examples
rubydata-structuresruby-hash

Using multiple classes together as a hash key


I found a script that calculates 3D models and combines identical vertices. It has the following logic, where according to my understanding, vertices are hash maps of vertex class:

unless vertices.key?(vertex)
  new_vertices << vertex
  vertices[ vertex ] = @vertex_index
  @vertex_index += 1
end

If we find a vertex that is unique, we add it to the new_vertices array.

I'd like to modify this so that the key for the hash map is a combination of a vertex and a material (both are classes from Sketchup, which is the software this script runs in). What is the best way to do this so that each key is a combination of two classes instead of one? Some sort of duple or class holding both the vertex and the material? Is that supported by a hash map?


Solution

  • In Ruby one might use whatever as hash key:

    hash = {
      42 => "an integer",
      [42, "forty two"] => "an array",
      Class => "a class"
    }
    #⇒  {42=>"an integer", [42, "forty two"]=>"an array", Class=>"a class"}
    
    hash[[42, "forty two"]]
    #⇒ "an array"
    

    That said, in your case you might use an array [vertex, material] as a key:

    unless vertices.key?([vertex, material])
      new_vertices_and_materials << [vertex, material]
      vertices[[vertex, material]] = @vertex_index
      @vertex_index += 1
    end
    

    The more rubyish approach would be to call Enumerable#uniq on the input and do:

    input = [ # example input data
      [:vertex1, :material1],
      [:vertex2, :material1],
      [:vertex2, :material1],
      [:vertex2, :material2],
      [:vertex2, :material2]
    ]
    new_vertices_and_materials = input.uniq
    vertices_and_materials_with_index =
      new_vertices_and_materials.
        zip(1..new_vertices_and_materials.size).
        to_h
    #⇒ {[:vertex1, :material1]=>1,
    #   [:vertex2, :material1]=>2,
    #   [:vertex2, :material2]=>3}