0

I am creating a hash containing lambdas as the value in Ruby. I want to access the key name in the lambda.

The hash is a collection of anonymous functions (lambdas) which take inputs and do a specific task. So, I am making a hash which draws a shape and so the keys in the hash are the shape names like circle, square, and the value for this key is a lambda which takes in inputs and executes some task which results in the shape drawn. So, here I want to print the name of the shape, i.e. the key, in the lambda.

The real example:

MARKER_TYPES = {
        # Default type is circle
        # Stroke width is set to 1
        nil: ->(draw, x, y, fill_color, border_color, size) {
          draw.stroke Rubyplot::Color::COLOR_INDEX[border_color]
          draw.fill Rubyplot::Color::COLOR_INDEX[fill_color]
          draw.circle(x,y, x + size,y)
        },
        circle: ->(draw, x, y, fill_color, border_color, size) {
          draw.stroke Rubyplot::Color::COLOR_INDEX[border_color]
          draw.fill Rubyplot::Color::COLOR_INDEX[fill_color]
          draw.circle(x,y, x + size,y)
        },
        plus: ->(draw, x, y, fill_color, border_color, size) {
          # size is length of one line
          draw.stroke Rubyplot::Color::COLOR_INDEX[fill_color]
          draw.line(x - size/2, y, x + size/2, y)
          draw.line(x, y - size/2, x, y + size/2)
        },
        dot: ->(draw, x, y, fill_color, border_color, size) {
          # Dot is a circle of size 5 pixels
          # size is kept 5 to make it visible, ideally it should be 1
          # which is the smallest displayable size
          draw.fill Rubyplot::Color::COLOR_INDEX[fill_color]
          draw.circle(x,y, x + 5,y)
        },
        asterisk: ->(draw, x, y, fill_color, border_color, size) {
          # Looks like a five sided star
          raise NotImplementedError, "marker #{self} has not yet been implemented"
        }
}

The Hash contains around 40 such key, value pairs.

The desired output of this would be marker star has not yet been implemented

A simple example:

HASH = {
  key1: ->(x) {
   puts('Number of key1 = ' + x.to_s) 
  }
}

Instead of hardcoding key1 I want to get the name of the key in the value which is a lambda as there are a lot of lambdas in the hash.

Replacing key1 with #{self} prints the class and not the name of the key.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
  • I see. No, the hash value doesn't know what it's key name is. How is the hash created? – lurker Jun 17 '19 at 22:52
  • You can get the key name by iterating over the hash, such as `HASH.each do |key, val|`. Can you show the full usage of this? Where do you invoke the lambas? – max pleaner Jun 17 '19 at 22:56
  • Edited the question, please let me know if still unclear. – AlishDipani Jun 17 '19 at 22:57
  • 1
    You said in your question, "there are a lot of lamdas in the hash". You only show one in your example. It would be helpful to understand how to distinguish them. Also, you haven't answered my question about how the hash is created. Is it done manually or is it generated? Based upon where you seem to be headed with this, perhaps a hash really isn't the right representation for what you're ultimately trying to do? Knowing ultimately what you're trying to achieve would help. – lurker Jun 17 '19 at 23:02
  • Edited it again. Yes, the hash is created manually. The hash is used in another function which takes in input as marker type (shape) and creates the marker(shape) of required type and at required coordinates. – AlishDipani Jun 17 '19 at 23:07
  • 2
    In answering questions on SO we run into many questions that are XY problems, where you ask about Y but should ask about X. "[What is the XY problem?](https://meta.stackexchange.com/q/66377)" Perhaps it'd be better to describe the intent; What are you trying to do, and why are you trying to do it this way? Also please see "[ask]" and the linked pages and "[mcve](https://stackoverflow.com/help/minimal-reproducible-example)" as they'll help you define the question better. – the Tin Man Jun 17 '19 at 23:19
  • This is looking more like you need a different approach to whatever your overall problem is. How does the function that uses the hash know what to pass for the `draw` argument? It would seem that knowledge of correspondence between the type of entity and draw method has to be known outside of the hash. This is looking a bit messy... – lurker Jun 17 '19 at 23:26
  • My question has been answered, Thank you for the suggestions, I will keep these in mind in the future! – AlishDipani Jun 17 '19 at 23:26
  • Ok, happy programming trails. – lurker Jun 17 '19 at 23:27
  • Yes! It is messy and the only problem is just knowing the name of the key in it's value and printing it, rest all the code works fine! – AlishDipani Jun 17 '19 at 23:27

2 Answers2

1

I don't think this can be done. A hash is a collection of keys and values. Keys are unique, values are not.You are asking a value (a lambda in this case, but it doesn't matter much) to which key it belongs. I could be one, none or many; there is no way for the value to "know". Ask the hash, not the value.

steenslag
  • 79,051
  • 16
  • 138
  • 171
  • The hash could be reversed into a hash of arrays so lookups could occur based on the values, but it'd be possible to get a list of possible lambdas, which would only another can of worms. – the Tin Man Jun 17 '19 at 23:14
0

You're trying to re-implement object oriented programming in Ruby. There's no need to, everything is an object in Ruby!

You can remove all the duplicated code, the lambdas and the complicated Hash logic:

class MarkerType
  attr_reader :name
  def initialize(name, &block)
    @name = name
    @block = block
  end

  def draw(*params)
    if @block
      @block.call(*params)
    else
      puts "Marker #{name} has not been implemented yet!"
    end
  end
end

square = MarkerType.new 'square' do |size=5|
  puts "Draw a square with size : #{size}"
end

square.draw
# => Draw a square with size : 5


asterisk = MarkerType.new 'asterisk'
asterisk.draw
# => Marker asterisk has not been implemented yet!
Eric Duminil
  • 52,989
  • 9
  • 71
  • 124