2

I have an array of hashes myhash:

myhash
=> [{"product_id"=>"1", "test"=>"2", "retest"=>"42"}, {"product_id"=>"1", "test"=>"2", "retest"=>"42"}]

I want to map the hashes to their value for product_id. I did this:

myhash.map{|item| item['product_id']}
# => ["1", "1"]

which gives what I want.

Is there anyway to make it nicer using a map proc? I tried myhash.map(&:fetch('product_id')), but to no avail.

Edit: In other words, I resumed the situation thanks to @7stud who tried to answer:

a.map(&:"fetch('product_id')")
=> NoMethodError: undefined method `fetch('product_id')' for {"product_id"=>"1", "test"=>"2", "retest"=>"42"}:Hash
     from (irb):5:in `map'
     from (irb):5
     from /home/shideneyu/.rbenv/versions/1.9.3-p194/bin/irb:12:in `<main>'

Or, when I do this:

{"product_id"=>"1", "test"=>"2", "retest"=>"42"}.fetch('product_id')
=> "1"

It retrieves the good value. The issue then is that I can't pass a param to the fetch method while using the map. How to do so?

sidney
  • 2,704
  • 3
  • 28
  • 44
  • There is no built in.. Create your own proc object... Look [this answer](http://stackoverflow.com/questions/23695653/can-you-supply-arguments-to-the-mapmethod-syntax-in-ruby/23711606#23711606) to get some idea about how to build.. – Arup Rakshit Jun 21 '14 at 19:14
  • Thanks you anyways, so I guess that the first working solution that I wrote is the simplest and cleaner then? – sidney Jun 21 '14 at 19:18
  • Ofcourse your one is fastest and clean too.. – Arup Rakshit Jun 21 '14 at 19:19
  • No offense, I like to use map(&:) when I can, @Arup Rakshit, I just wondered why that fetch didn't work. I still have no answer on it between. – sidney Jun 21 '14 at 19:28
  • 1
    @sawa, sorry, it's a remain of my irb tests, I edited my question. – sidney Jun 21 '14 at 19:29

1 Answers1

2

I like to use map(&:) when I can, @Arup Rakshit, I just wondered why that fetch didn't work. I still have no answer on it between.

When you write:

:fetch('product_id')

You are informing ruby that everything after the colon is the name of your symbol. Well, there are certain rules for symbol names, and this:

fetch('product_id')

violates the syntax for symbols. Why? Because it looks like a method call, and therefore it is not a valid symbol name. If you absolutely have to have that symbol name, you could create it by using quotes:

:"fetch('product_id')"

...but of course that is not what you are trying to do.

Next, what does & do? It does two things:

  1. & calls Symbol#to_proc on the symbol, which returns a proc, something like this:

    {|x| x.send(the_symbol) } #where x is an element of the array

  2. & turns the proc into a block which is supplied to the method being called, e.g. map() in your example.

7stud
  • 46,922
  • 14
  • 101
  • 127
  • Thanks for the kind explanation, in this case, `x` is the element of the array, and therefore it is a hash. `fetch` is a hash method ( http://www.ruby-doc.org/core-2.1.2/Hash.html#method-i-fetch ) Did I miss something? – sidney Jun 21 '14 at 21:37
  • @sidney, You are telling ruby there is a method named `fetch('product_id')`. That method name has 19 characters to it. Please post a link showing a method name for Hash that begins with an 'f' and has 19 characters. – 7stud Jun 21 '14 at 21:41
  • so, I can call `fetch`, but I can't pass any params to this method? – sidney Jun 21 '14 at 21:42
  • I edited the question for more clarity thanks to you. – sidney Jun 21 '14 at 21:45
  • 1
    @sidney, *You* cannot pass parameters to the proc because it is being converted to a block by & for the map() method, so that means map() is the thing that passes arguments to the block...and map() only passes one argument. However, there are other methods that pass more than one argument to a block, e.g. inject(), but this opens a can of worms because Rails#Symbol#to_proc is implemented differently than Ruby#Symbol#to_proc. Looking at the C source code for Ruby#Symbol#to_proc, I don't believe you can pass it more than one argument, ... – 7stud Jun 21 '14 at 21:51
  • ...so as far as I can tell, in Ruby & only works on no-arg method names, e.g. capitalize, to_i, etc. – 7stud Jun 21 '14 at 21:53
  • Thanks you, I will probably accept the answer when I'll be 100% sure of that, I will do some researches ;) – sidney Jun 21 '14 at 21:54
  • 1
    @sidney, If you look at the 3rd party library ampex: http://cirw.in/blog/ampex, it adds the ability to pass arguments when using &, and one of the examples does exactly what you want to do. – 7stud Jun 21 '14 at 21:58
  • Indeed, that seems pretty nice. Unfortunately it is a 3rd party. I would have liked that it was native. I won't use it since it is a dependency, but well, at least I'm glad someone thought of this issue =) – sidney Jun 21 '14 at 22:04
  • @sydney, I would expect ampex to be incorporated into ruby at some point, although I imagine there are some people that aren't too happy with the perlish `&:meth_name` syntax to begin with. I know I don't like it. – 7stud Jun 21 '14 at 22:09
  • that is good to know @7stud, but I think that this is the way to go even if it is not rubyish, in order to get rid of those hurdles that prevents us to code naturally. If today we can use `&:` and not its opposite, well, that's not awesome since I had to ask the question here. (It seems a little pretentious because I forgot the ' " ', but even though, I'm dead serious :) ). – sidney Jun 21 '14 at 22:15