Iterate hash to make another hash

Hi folks. I can’t figure out a puzzle where I need to generate a Sequel hash from a reference hash and an array:

reference hash:

t = {:name => :company_name, :rank => :company_rank, :serial_no => :company_serial}

I want to iterate through each of those pairs and reference the value of each, into the following Sequel command:

companies.insert(t[0] => myarray[0], t[1] => myarray[1], t[2] => myarray[2])

myArray is another set of items. I can’t get my head around how I can provide some kind of iteration and call out the sequence of values from both items, into yet another hash for that .insert method.

Any insight appreciated.

Cheers, Bee

If I understand correctly, myarray contains values for company_name,
company_rank and company_serial in that order. Then you can do the
following:

2.2.1 :001 > t = {:name => :company_name, :rank => :company_rank,
:serial_no => :company_serial}
=> {:name=>:company_name, :rank=>:company_rank, :serial_no=>:company_serial}
2.2.1 :003 > my_array = ["my company name", "my company rank", "my
company serial"]
=> ["my company name", "my company rank", "my company serial"]
2.2.1 :004 > t.values_at(:name, :rank, :serial_no).zip(my_array)
=> [[:company_name, "my company name"], [:company_rank, "my company
rank"], [:company_serial, "my company serial"]]
2.2.1 :006 > Hash[*t.values_at(:name, :rank, :serial_no).zip(my_array).flatten]
=> {:company_name=>"my company name", :company_rank=>"my company
rank", :company_serial=>"my company serial"}

This is a way. First you extract an array of values from the Hash with
values_at. Since the iteration order of the Hash might not be what you
want, it's better to explicitly choose the values you want in order.
See this example:

2.2.1 :008 > t.each {|k,v| puts [k,v].join(" => ")}
name => company_name
rank => company_rank
serial_no => company_serial
=> {:name=>:company_name, :rank=>:company_rank, :serial_no=>:company_serial}
2.2.1 :009 > h = {}
=> {}
2.2.1 :010 > h[:rank] = :company_rank
=> :company_rank
2.2.1 :011 > h[:serial_no] = :company_serial
=> :company_serial
2.2.1 :012 > h[:name] = :company_name
=> :company_name
2.2.1 :013 > h.each {|k,v| puts [k,v].join(" => ")}
rank => company_rank
serial_no => company_serial
name => company_name
=> {:rank=>:company_rank, :serial_no=>:company_serial, :name=>:company_name}

Hashes in Ruby iterate over the "insertion order" and for this use
case, it might be a good idea to not rely on that order.

Next, we use zip to pair the array coming from the Hash with myarray.
Then we use the Hash class method to construct a hash with a list
of pairs in order, which we get using flatten, because zip returns an
array of arrays.

Hope this helps,

Jesus.

···

On Thu, Sep 29, 2016 at 6:08 PM, Bee.Lists <bee.lists@gmail.com> wrote:

Hi folks. I can’t figure out a puzzle where I need to generate a Sequel hash from a reference hash and an array:

reference hash:

t = {:name => :company_name, :rank => :company_rank, :serial_no => :company_serial}

I want to iterate through each of those pairs and reference the value of each, into the following Sequel command:

companies.insert(t[0] => myarray[0], t[1] => myarray[1], t[2] => myarray[2])

myArray is another set of items. I can’t get my head around how I can provide some kind of iteration and call out the sequence of values from both items, into yet another hash for that .insert method.

Any insight appreciated.

As has been discussed already you reliance on order in myarray is
fragile. Basically there is a mapping missing from the Hash to the
Array. For example:

t = {:name => 0, :rank => 2, :serial_no => 1}
companies.insert(t.each_with_object({}) {|(k,v),h| h[k] = myarray[v]})

Kind regards

robert

···

On Thu, Sep 29, 2016 at 6:08 PM, Bee.Lists <bee.lists@gmail.com> wrote:

Hi folks. I can’t figure out a puzzle where I need to generate a Sequel hash from a reference hash and an array:

reference hash:

t = {:name => :company_name, :rank => :company_rank, :serial_no => :company_serial}

I want to iterate through each of those pairs and reference the value of each, into the following Sequel command:

companies.insert(t[0] => myarray[0], t[1] => myarray[1], t[2] => myarray[2])

myArray is another set of items. I can’t get my head around how I can provide some kind of iteration and call out the sequence of values from both items, into yet another hash for that .insert method.

Any insight appreciated.

--
[guy, jim, charlie].each {|him| remember.him do |as, often| as.you_can
- without end}
http://blog.rubybestpractices.com/

Afternoon,

Hi folks. I can’t figure out a puzzle where I need to generate a Sequel
hash from a reference hash and an array:

reference hash:

t = {:name => :company_name, :rank => :company_rank, :serial_no =>
:company_serial}

I want to iterate through each of those pairs and reference the value of
each, into the following Sequel command:

companies.insert(t[0] => myarray[0], t[1] => myarray[1], t[2] =>
myarray[2])

A tad off-topic - but I believe you could just do

companies.insert(t.values, myarray)

as the Sequel docs here -
http://sequel.jeremyevans.net/rdoc/classes/Sequel/Dataset.html#method-i-insert
- show that the insert method will take 2 arrays (columns and values) and
work just fine.

Not that learning what you asked about isn't cool - but other options are
cool as well :slight_smile:

John

···

On Thu, Sep 29, 2016 at 9:08 AM, Bee.Lists <bee.lists@gmail.com> wrote:

Hi Jesus. Thanks for the reply.

This command seems to be the one I’d be looking for:

t.each {|k,v| puts [k,v].join(" => “)}

From first glance, it seems like it’s just an output, but that construction actually results in a properly formed hash. Interesting.

But you brought up another point of hash value sequencing, which I need to have in the proper order. I think I’m going to have to rethink all this.

···

On Sep 29, 2016, at 12:25 PM, Jesús Gabriel y Galán <jgabrielygalan@gmail.com> wrote:

2.2.1 :001 > t = {:name => :company_name, :rank => :company_rank,
:serial_no => :company_serial}
=> {:name=>:company_name, :rank=>:company_rank, :serial_no=>:company_serial}
2.2.1 :003 > my_array = ["my company name", "my company rank", "my
company serial"]
=> ["my company name", "my company rank", "my company serial"]
2.2.1 :004 > t.values_at(:name, :rank, :serial_no).zip(my_array)
=> [[:company_name, "my company name"], [:company_rank, "my company
rank"], [:company_serial, "my company serial"]]
2.2.1 :006 > Hash[*t.values_at(:name, :rank, :serial_no).zip(my_array).flatten]
=> {:company_name=>"my company name", :company_rank=>"my company
rank", :company_serial=>"my company serial"}

This is a way. First you extract an array of values from the Hash with
values_at. Since the iteration order of the Hash might not be what you
want, it's better to explicitly choose the values you want in order.
See this example:

2.2.1 :008 > t.each {|k,v| puts [k,v].join(" => ")}
name => company_name
rank => company_rank
serial_no => company_serial
=> {:name=>:company_name, :rank=>:company_rank, :serial_no=>:company_serial}
2.2.1 :009 > h = {}
=> {}
2.2.1 :010 > h[:rank] = :company_rank
=> :company_rank
2.2.1 :011 > h[:serial_no] = :company_serial
=> :company_serial
2.2.1 :012 > h[:name] = :company_name
=> :company_name
2.2.1 :013 > h.each {|k,v| puts [k,v].join(" => ")}
rank => company_rank
serial_no => company_serial
name => company_name
=> {:rank=>:company_rank, :serial_no=>:company_serial, :name=>:company_name}

Hashes in Ruby iterate over the "insertion order" and for this use
case, it might be a good idea to not rely on that order.

Next, we use zip to pair the array coming from the Hash with myarray.
Then we use the Hash class method to construct a hash with a list
of pairs in order, which we get using flatten, because zip returns an
array of arrays.

Cheers, Bee

Hi Jesus. Thanks for the reply.

This command seems to be the one I’d be looking for:

t.each {|k,v| puts [k,v].join(" => “)}

From first glance, it seems like it’s just an output, but that construction actually results in a properly formed hash. Interesting.

Well, the puts there was to show that you have to be careful about the
iteration order.

But you brought up another point of hash value sequencing, which I need to have in the proper order. I think I’m going to have to rethink all this.

If you need the fields from the hash in a specific order it's better
to explicitly ask for that with values_at. If you don't know in
advance the contents of the Hash and have to dynamically create the
query, yes, you might need to rethink the strategy again to be sure
the order is correct.

Jesus.

···

On Thu, Sep 29, 2016 at 6:38 PM, Bee.Lists <bee.lists@gmail.com> wrote:

I moved to a different approach as I didn’t need one compound data type to accomplish what I needed.

···

On Oct 1, 2016, at 5:26 PM, John W Higgins <wishdev@gmail.com> wrote:

A tad off-topic - but I believe you could just do

companies.insert(t.values, myarray)

as the Sequel docs here - Sequel::Dataset - show that the insert method will take 2 arrays (columns and values) and work just fine.

Not that learning what you asked about isn't cool - but other options are cool as well :slight_smile:

Cheers, Bee

You continue to not see the forest for the trees

Lets assume you are 100% correct

t.each {|k,v| [k,v].join(" => “)}

returns a hash and not self

what should the following return?

t.each {|k,v| [v, k].join(" => “)}
(note k and v inverted in the array)

It should return, under your conceptual idea, an inverted hash with the
keys as the values and vice-versa.

Does it?

John

···

On Thu, Sep 29, 2016 at 9:38 AM, Bee.Lists <bee.lists@gmail.com> wrote:

Hi Jesus. Thanks for the reply.

This command seems to be the one I’d be looking for:

t.each {|k,v| puts [k,v].join(" => “)}

From first glance, it seems like it’s just an output, but that
construction actually results in a properly formed hash. Interesting.

But you brought up another point of hash value sequencing, which I need to
have in the proper order. I think I’m going to have to rethink all this.

> On Sep 29, 2016, at 12:25 PM, Jesús Gabriel y Galán < > jgabrielygalan@gmail.com> wrote:
>
> 2.2.1 :001 > t = {:name => :company_name, :rank => :company_rank,
> :serial_no => :company_serial}
> => {:name=>:company_name, :rank=>:company_rank,
:serial_no=>:company_serial}
> 2.2.1 :003 > my_array = ["my company name", "my company rank", "my
> company serial"]
> => ["my company name", "my company rank", "my company serial"]
> 2.2.1 :004 > t.values_at(:name, :rank, :serial_no).zip(my_array)
> => [[:company_name, "my company name"], [:company_rank, "my company
> rank"], [:company_serial, "my company serial"]]
> 2.2.1 :006 > Hash[*t.values_at(:name, :rank, :serial_no).zip(my_array).
flatten]
> => {:company_name=>"my company name", :company_rank=>"my company
> rank", :company_serial=>"my company serial"}
>
> This is a way. First you extract an array of values from the Hash with
> values_at. Since the iteration order of the Hash might not be what you
> want, it's better to explicitly choose the values you want in order.
> See this example:
>
> 2.2.1 :008 > t.each {|k,v| puts [k,v].join(" => ")}
> name => company_name
> rank => company_rank
> serial_no => company_serial
> => {:name=>:company_name, :rank=>:company_rank,
:serial_no=>:company_serial}
> 2.2.1 :009 > h = {}
> => {}
> 2.2.1 :010 > h[:rank] = :company_rank
> => :company_rank
> 2.2.1 :011 > h[:serial_no] = :company_serial
> => :company_serial
> 2.2.1 :012 > h[:name] = :company_name
> => :company_name
> 2.2.1 :013 > h.each {|k,v| puts [k,v].join(" => ")}
> rank => company_rank
> serial_no => company_serial
> name => company_name
> => {:rank=>:company_rank, :serial_no=>:company_serial,
:name=>:company_name}
>
> Hashes in Ruby iterate over the "insertion order" and for this use
> case, it might be a good idea to not rely on that order.
>
> Next, we use zip to pair the array coming from the Hash with myarray.
> Then we use the Hash class method to construct a hash with a list
> of pairs in order, which we get using flatten, because zip returns an
> array of arrays.

Cheers, Bee

Unsubscribe: <mailto:ruby-talk-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-talk&gt;

Unless I'm misunderstanding, nothing is automatically coerced into a hash
here. t is already the right hash; the each iteration prints out each pair,
and returns t at the end, unchanged.

···

On Thu, Sep 29, 2016 at 1:04 PM, Bee.Lists <bee.lists@gmail.com> wrote:

> On Sep 29, 2016, at 12:41 PM, Jesús Gabriel y Galán < > jgabrielygalan@gmail.com> wrote:
>
>> From first glance, it seems like it’s just an output, but that
construction actually results in a properly formed hash. Interesting.
>
> Well, the puts there was to show that you have to be careful about the
> iteration order.

Yes. Even so, I’m still hung up on that it is automatically coerced into
a hash. Useful tool, that.

--
        Eric Christopherson

From first glance, it seems like it’s just an output, but that construction actually results in a properly formed hash. Interesting.

Well, the puts there was to show that you have to be careful about the
iteration order.

Yes. Even so, I’m still hung up on that it is automatically coerced into a hash. Useful tool, that.

But you brought up another point of hash value sequencing, which I need to have in the proper order. I think I’m going to have to rethink all this.

If you need the fields from the hash in a specific order it's better
to explicitly ask for that with values_at. If you don't know in
advance the contents of the Hash and have to dynamically create the
query, yes, you might need to rethink the strategy again to be sure
the order is correct.

Yeah the application I’m using this for is a bit abstract, so I’ve changed to a single array, as the first reference hash isn’t really needed. So now abstraction with minimal interaction is just fine. Simple is good. Heh.

Cheers, Bee

···

On Sep 29, 2016, at 12:41 PM, Jesús Gabriel y Galán <jgabrielygalan@gmail.com> wrote:

Joining using the string “ => “ results in a hash. It’s implied that the final structure is indeed a hash.

···

On Sep 29, 2016, at 2:12 PM, Eric Christopherson <echristopherson@gmail.com> wrote:

Unless I'm misunderstanding, nothing is automatically coerced into a hash here. t is already the right hash; the each iteration prints out each pair, and returns t at the end, unchanged.

Cheers, Bee

No, this is not the case on either sentence.

Joining using the string results in a string, not a hash. It might LOOK like a hash when you're printing it, but that's it.

The final structure you're seeing is your initial structure because #each returns self.

···

On Sep 29, 2016, at 13:03, Bee.Lists <bee.lists@gmail.com> wrote:

Joining using the string “ => “ results in a hash. It’s implied that the final structure is indeed a hash.

That’s exactly my point. But if you test it, it does indeed come out as a hash. Testing for class on that final result is indeed a hash. That’s the part I was saying was surprising and very useful.

···

On Sep 30, 2016, at 5:36 PM, Ryan Davis <ryand-ruby@zenspider.com> wrote:

No, this is not the case on either sentence.

Joining using the string results in a string, not a hash. It might LOOK like a hash when you're printing it, but that's it.

The final structure you're seeing is your initial structure because #each returns self.

Cheers, Bee

Going back to this code:

···

On 1 October 2016 at 12:39, Bee.Lists <bee.lists@gmail.com> wrote:

That’s exactly my point. But if you test it, it does indeed come out as a
hash. Testing for class on that final result is indeed a hash. That’s the
part I was saying was surprising and very useful.

~~~
t.each {|k,v| puts [k,v].join(" => ")}
~~~

There are some important points to note:

1. The result of this expression is not used. It's not being assigned
anywhere, or passed as a parameter to a function call, or anything. In
some languages we'd say it's being evaluated in a void context.

2. The only side-effect of this expression is that characters are written
to STDOUT using the `puts` function. No object are being mutated, no data
structures are being created, no *new* value is being returned. The *only*
thing it does is print characters to STDOUT.

3. Hash#each called with a block explicitly returns the original hash[1].
If this expression wasn't called in a void context -- if the resulting
value was captured or used -- it would be `t`.

Effectively you could replace this:

~~~
companies.insert( t.each {|k,v| puts [k,v].join(" => ")} )
~~~

with this:

~~~
companies.insert( t )
~~~

and the only difference would be that nothing is printed to STDOUT in the
second version. There is no magic coercion. This is what Eric
Christopherson said earlier in this thread. Please move more slowly -- *read
*the documentation for the functions you're using, *read *the code,
formulate *meaningful *ways to test your hypotheses, and listen to the
advice given to you by the people from whom you sought it.

Cheers

[1]: Class: Hash (Ruby 2.3.1)
--
  Matthew Kerwin
  http://matthew.kerwin.net.au/

Excellent advice! Bee, I might add, if you do not understand how
something works, test it in IRB or a small script. If you try to
understand complex expressions, break them up into smaller parts and
look at their results.

Kind regards

robert

···

On Sat, Oct 1, 2016 at 5:48 AM, Matthew Kerwin <matthew@kerwin.net.au> wrote:

Please move more slowly -- read
the documentation for the functions you're using, read the code, formulate
meaningful ways to test your hypotheses, and listen to the advice given to
you by the people from whom you sought it.

--
[guy, jim, charlie].each {|him| remember.him do |as, often| as.you_can
- without end}
http://blog.rubybestpractices.com/

Hi Robert. Some advice. Don’t assume I didn’t do that. I’ve been doing stuff like that since the 80’s. Also Matthew, don’t assume I didn’t read the notes on this.

Joining two objects with a string, which by the documentation, results in a string. Convenient that it a “ => “ and results in a hash, does indeed go against any documentation.

···

On Oct 1, 2016, at 4:43 AM, Robert Klemme <shortcutter@googlemail.com> wrote:

Excellent advice! Bee, I might add, if you do not understand how
something works, test it in IRB or a small script. If you try to
understand complex expressions, break them up into smaller parts and
look at their results.

Kind regards

robert

Cheers, Bee

Hi Bee,
The RETURNED object (t) is a hash, because the prints dont modify the
object, which initially was a hash, as Matthew and Robert said.
print "xx => yy" doesnt return a hash, documentation is correct.
If you are convinced you're right, tell an example with detailed doku, than
we will find your mistake, or correct the documentation :wink:

Bye
Berg

There is no print in any of this. Second, asking a hash for a key or a value both return a string. String plus string plus string, should result in a string, as per documentation.

···

On Oct 1, 2016, at 12:41 PM, A Berger <aberger7890@gmail.com> wrote:

Hi Bee,
The RETURNED object (t) is a hash, because the prints dont modify the object, which initially was a hash, as Matthew and Robert said.
print "xx => yy" doesnt return a hash, documentation is correct.
If you are convinced you're right, tell an example with detailed doku, than we will find your mistake, or correct the documentation :wink:

Cheers, Bee

Module: Kernel (Ruby 2.3.0) which leads to
Class: IO (Ruby 2.3.0) which says: "
Writes the given objects to *ios* as with
IO#print
.
"

Asking a hash for a key or a value both return whatever object is used in
the hash's key or value. If those are Strings, then it returns Strings.

···

On 2 October 2016 at 09:19, Bee.Lists <bee.lists@gmail.com> wrote:

There is no print in any of this. Second, asking a hash for a key or a
value both return a string. String plus string plus string, should result
in a string, as per documentation.

~~~
hsh = { 0 => 3.14, :foo => Object.new }
hsh[0].class #=> Float
hsh.keys[1].class #=> Symbol

# and finally
hsh.each{nil} == hsh #=> true
~~~

​Yes, joining two Strings with a String results in a String... which is
then printed using `puts`. It's not returned in any way. I know I've said
this already, and I assume you've understood even though you haven't
acknowledged it (which is why I've repeated it again here.) I just want to
make sure the original issue is resolved and clear.

I’ve been doing stuff like that since the 80’s.

I'm glad you've been programming since I was a wee babby. I've only been
doing it since the 90s (Ruby for about a decade.) That's not relevant,
though.

Also Matthew, don’t assume I didn’t read the notes on this.

The documentation for Hash#each
<Class: Hash (Ruby 2.3.0); shows, somewhat
abstractly perhaps but consistent with the official Ruby docs idioms, that
it returns the receiver unmodified. Hence my request that you *read* (with
emphasis) the documentation. (And they're not notes, they're the official
human-readable contract for how the core libraries' APIs behave.)

The resulting object passes the `.class == Hash` test, but a deeper
inspection would have shown that the result comes out unmodified by `each`;
for example: `p(
t.each {|k,v| puts [
​v, k
].join(" => ")}
​ )​
​` would have shown that the final result is identical to `
t
`, even though I flipped the `
k
` and `
v
` inside the loop. Hence my request that you formulate *meaningful* ways to
test hypotheses. Testing a noop that, even if it did something would still
end up being a noop, is not very useful. Also note that `
[p,k].join("=>")
` removes all the quotation marks that would have made it parseable as a
Hash of Strings.

Please don't take my advice as admonishment (unless you've a guilty
conscience, and even then...) and certainly don't dismiss me because of
your life experiences -- fine whisky improves with age, but in the tech
world it's far more common for the synonym of "old" to be "obsolete."
Asking for advice on these mailing lists is a great thing; accepting the
advice is the hard part.

Cheers
--
  Matthew Kerwin
  http://matthew.kerwin.net.au/

There is no print in any of this. Second, asking a hash for a key or a value both return a string. String plus string plus string, should result in a string, as per documentation.

Module: Kernel (Ruby 2.3.0) which leads to Class: IO (Ruby 2.3.0) which says: "Writes the given objects to ios as with IO#print."

Asking a hash for a key or a value both return whatever object is used in the hash's key or value. If those are Strings, then it returns Strings.

A join on two symbols and an object which, in this case, was decidedly a string. The result is a hash because the string contains “ => “?

~~~
hsh = { 0 => 3.14, :foo => Object.new }
hsh[0].class #=> Float
hsh.keys[1].class #=> Symbol

# and finally
hsh.each{nil} == hsh #=> true
~~~

​Yes, joining two Strings with a String results in a String... which is then printed using `puts`. It's not returned in any way. I know I've said this already, and I assume you've understood even though you haven't acknowledged it (which is why I've repeated it again here.) I just want to make sure the original issue is resolved and clear.

The puts has nothing to do with any of this. You can repeat it until next week, but it has absolutely nothing to do with any of this. Nothing. I’ll repeat myself: nothing. The original issue is not what was presented via the puts (or print, as somebody else said, which was never part of any discussion…but we move on). It was about the result through the join.

> I’ve been doing stuff like that since the 80’s.

I'm glad you've been programming since I was a wee babby. I've only been doing it since the 90s (Ruby for about a decade.) That's not relevant, though.

Well, you see, it is. First, you just brought it up. Second, don’t assume that everybody else is beneath you.

> Also Matthew, don’t assume I didn’t read the notes on this.

The documentation for Hash#each shows, somewhat abstractly perhaps but consistent with the official Ruby docs idioms, that it returns the receiver unmodified. Hence my request that you read (with emphasis) the documentation. (And they're not notes, they're the official human-readable contract for how the core libraries' APIs behave.)

The resulting object passes the `.class == Hash` test, but a deeper inspection would have shown that the result comes out unmodified by `each`; for example: `p( t.each {|k,v| puts [​v, k].join(" => ")}​ )​​` would have shown that the final result is identical to `t`, even though I flipped the `k` and `v` inside the loop. Hence my request that you formulate meaningful ways to test hypotheses. Testing a noop that, even if it did something would still end up being a noop, is not very useful. Also note that `[p,k].join("=>")` removes all the quotation marks that would have made it parseable as a Hash of Strings.

So you’re saying they don’t have a data type. Right. That makes no sense at all.

Please don't take my advice as admonishment (unless you've a guilty conscience, and even then...) and certainly don't dismiss me because of your life experiences -- fine whisky improves with age, but in the tech world it's far more common for the synonym of "old" to be "obsolete." Asking for advice on these mailing lists is a great thing; accepting the advice is the hard part.

Please don’t take my correction as an indication you are wrong. Older people are the ones you received any education from. Second, your “advice” holds no water, considering the case. Two strings or symbols, joined with a string, returns a hash, and I said it was interesting and useful. You insist on talking about the puts, which is completely irrelevant, but you feel the need to repeat yourself.

Have a nice day.

Cheers, Bee

···

On Oct 1, 2016, at 8:15 PM, Matthew Kerwin <matthew@kerwin.net.au> wrote:
On 2 October 2016 at 09:19, Bee.Lists <bee.lists@gmail.com> wrote: