Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Ruby > Merging hashes using both symbols and strings as keys

Reply
Thread Tools

Merging hashes using both symbols and strings as keys

 
 
shenry
Guest
Posts: n/a
 
      10-30-2009
I'm trying to merge to hashes, one using symbols as keys (the defined
default values for my class) and the other using strings as keys
(taken from the params hash).

default = { :name => "Joe", :age => 50 }

params = { "name" => "Bill" }

new_hash = default.merge(params)
>> { :name => "Joe", :age => 50, "name" => "Bill }


What's the Ruby way to handle this so that it overwrites :name with
"name"? Do I need to implement a stringify_keys or symbolize_keys
method like in Rails? I'd like to avoid using strings as the keys in
my default hash.

Any help greatly appreciated.

Stu
 
Reply With Quote
 
 
 
 
Marnen Laibow-Koser
Guest
Posts: n/a
 
      10-30-2009
shenry wrote:
> I'm trying to merge to hashes, one using symbols as keys (the defined
> default values for my class) and the other using strings as keys
> (taken from the params hash).
>
> default = { :name => "Joe", :age => 50 }
>
> params = { "name" => "Bill" }
>
> new_hash = default.merge(params)
>>> { :name => "Joe", :age => 50, "name" => "Bill }

>
> What's the Ruby way to handle this so that it overwrites :name with
> "name"? Do I need to implement a stringify_keys or symbolize_keys
> method like in Rails? I'd like to avoid using strings as the keys in
> my default hash.


I would think it would be easiest to use something like symbolize_keys.
Or just lift the whole HashWithIndifferentAccess class from Rails.

>
> Any help greatly appreciated.
>
> Stu


Best,
--
Marnen Laibow-Koser
http://www.marnen.org

--
Posted via http://www.ruby-forum.com/.

 
Reply With Quote
 
 
 
 
7stud --
Guest
Posts: n/a
 
      10-30-2009
shenry wrote:
> I'd like to avoid using strings as the keys in
> my default hash.
>


h1 = { :name => "Joe", :age => 50 }
h2 = { "name" => "Bill", hone => "123-4567"}

h2.each do |key, val|
h1[key.to_sym] = val
end

p h1

--output:--
{:age=>50, hone=>"123-4567", :name=>"Bill"}

--
Posted via http://www.ruby-forum.com/.

 
Reply With Quote
 
Intransition
Guest
Posts: n/a
 
      11-01-2009


On Oct 30, 12:10=A0pm, shenry <stuarthe...@gmail.com> wrote:
> I'm trying to merge to hashes, one using symbols as keys (the defined
> default values for my class) and the other using strings as keys
> (taken from the params hash).
>
> default =3D { :name =3D> "Joe", :age =3D> 50 }
>
> params =3D { "name" =3D> "Bill" }
>
> new_hash =3D default.merge(params)
>
> >> { :name =3D> "Joe", :age =3D> 50, "name" =3D> "Bill }

>
> What's the Ruby way to handle this so that it overwrites :name with
> "name"? Do I need to implement a stringify_keys or symbolize_keys
> method like in Rails? I'd like to avoid using strings as the keys in
> my default hash.
>
> Any help greatly appreciated.


require 'facets/hash/rekey'

default.merge(params.rekey)

rekey takes a block, without a block it is the same as:

rekey(&:to_sym)


 
Reply With Quote
 
Robert Klemme
Guest
Posts: n/a
 
      11-02-2009
2009/10/30 7stud -- <>:
> shenry wrote:
>> I'd like to avoid using strings as the keys in
>> my default hash.
>>

>
> h1 =3D =A0{ :name =3D> "Joe", :age =3D> 50 }
> h2 =3D { "name" =3D> "Bill", hone =3D> "123-4567"}
>
> h2.each do |key, val|
> =A0h1[key.to_sym] =3D val
> end
>
> p h1
>
> --output:--
> {:age=3D>50, hone=3D>"123-4567", :name=3D>"Bill"}


Typically you do not want to modify defaults so I'd probably do

irb(main):001:0> default =3D { :name =3D> "Joe", :age =3D> 50 }.freeze
=3D> {:name=3D>"Joe", :age=3D>50}
irb(main):002:0> params =3D { "name" =3D> "Bill" }
=3D> {"name"=3D>"Bill"}
irb(main):003:0> new_hash =3D default.dup
=3D> {:name=3D>"Joe", :age=3D>50}
irb(main):004:0> params.each {|k,v| new_hash[k.to_sym]=3Dv}
=3D> {"name"=3D>"Bill"}
irb(main):005:0> new_hash
=3D> {:name=3D>"Bill", :age=3D>50}

If you are allowed to change params you could do

irb(main):001:0> default =3D { :name =3D> "Joe", :age =3D> 50 }.freeze
=3D> {:name=3D>"Joe", :age=3D>50}
irb(main):002:0> params =3D { "name" =3D> "Bill" }
=3D> {"name"=3D>"Bill"}
irb(main):003:0> default.each {|k,v| params[k.to_s] ||=3D v}
=3D> {:name=3D>"Joe", :age=3D>50}
irb(main):004:0> params
=3D> {"name"=3D>"Bill", "age"=3D>50}

Kind regards

robert


--=20
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

 
Reply With Quote
 
Josh Cheek
Guest
Posts: n/a
 
      11-03-2009
[Note: parts of this message were removed to make it a legal post.]

On Fri, Oct 30, 2009 at 11:10 AM, shenry <> wrote:

> I'm trying to merge to hashes, one using symbols as keys (the defined
> default values for my class) and the other using strings as keys
> (taken from the params hash).
>
> default = { :name => "Joe", :age => 50 }
>
> params = { "name" => "Bill" }
>
> new_hash = default.merge(params)
> >> { :name => "Joe", :age => 50, "name" => "Bill }

>
> What's the Ruby way to handle this so that it overwrites :name with
> "name"? Do I need to implement a stringify_keys or symbolize_keys
> method like in Rails? I'd like to avoid using strings as the keys in
> my default hash.
>
> Any help greatly appreciated.
>
> Stu
>
>


Hi, I made a small module called SymbolizeKeys that will allow you to extend
the hash you are interested in applying this behaviour to, you can then use
it like this:

default = { :name => "Joe", :age => 50 }.extend(SymbolizeKeys)

params = { "name" => "Bill" }

default.merge(params) # => {:age=>50, :name=>"Bill"}


I'm going through "Ruby Best Practices" right now, which touches on testing
in the first chapter. This seemed like a good opportunity to practice that
(while I eagerly await PragProg's RSpec book), so I'll include the tests I
wrote.

If anyone has relevant thoughts / criticisms, I welcome them (I don't
promise to agree, though). Is extending the object a wise approach? Are my
tests appropriate / follow good testing methodologies? Is there a better way
to implement anything I've done?




# file: symbolize_keys.rb

module SymbolizeKeys

# converts any current string keys to symbol keys
def self.extended(hash)
hash.each do |key,value|
if key.is_a?(String)
hash.delete key
hash[key] = value #through overridden []=
end
end
end

# assigns a new key/value pair
# converts they key to a symbol if it is a string
def []=(*args)
args[0] = args[0].to_sym if args[0].is_a?(String)
super
end

# returns new hash which is the merge of self and other hashes
# the returned hash will also be extended by SymbolizeKeys
def merge(*other_hashes , &resolution_proc )
merged = Hash.new.extend SymbolizeKeys
merged.merge! self , *other_hashes , &resolution_proc
end

# merges the other hashes into self
# if a proc is submitted , it's return will be the value for the key
def merge!( *other_hashes , &resolution_proc )

# default resolution: value of the other hash
resolution_proc ||= proc{ |key,oldval,newval| newval }

# merge each hash into self
other_hashes.each do |hash|
hash.each{ |k,v|
# assign new k/v into self, resolving conflicts with resolution_proc
self[k] = self.has_key?(k) ? resolution_proc[k,self[k],v] : v
}
end

self
end

end


--------------------------------------------------


# file: symbolize_keys_test.rb

require 'test/unit'
require 'symbolize_keys'

# this method was written by Gregory Brown
# and comes from
http://github.com/sandal/rbp/blob/7d..._extensions.rb
module Test::Unit
# Used to fix a minor minitest/unit incompatibility in flexmock
AssertionFailedError = Class.new(StandardError)

class TestCase

def self.must(name, &block)
test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym
defined = instance_method(test_name) rescue false
raise "#{test_name} is already defined in #{self}" if defined
if block_given?
define_method(test_name, &block)
else
define_method(test_name) do
flunk "No implementation provided for #{name}"
end
end
end

end
end


class ExtendingWithSymbolizeKeysTest < Test::Unit::TestCase
def setup
@default = {
:age => 50 ,
'initially a string' => 51 ,
/neither string nor symbol/ => 52 ,
}
@default.extend SymbolizeKeys
end

must "convert string keys to symbols when extended" do
assert_nil @default[ 'initially a string']
assert_equal @default[:'initially a string'] , 51
end

must "leave symbol keys as symbols" do
assert_equal @default[:age] , 50
end

must 'leave non symbols / strings as they are' do
assert_equal @default[/neither string nor symbol/] , 52
end

end


class SettingKeysWithSymbolizeKeysTest < Test::Unit::TestCase
def setup
@default = Hash.new.extend SymbolizeKeys

end

must "convert string keys to symbols" do
@default['foo'] = :bar
assert_equal @default[:foo] , :bar
end

must "leave symbol keys as symbols" do
@default[:foo] = :bar
assert_equal @default[:foo] , :bar
end

must 'leave non symbols / strings as they are' do
@default[/foo/] = :bar
assert_equal @default[/foo/] , :bar
end

end


class MergingWithSymbolizeKeysTest < Test::Unit::TestCase
def setup
@default = {
:name => 'Joe' ,
:age => 50 ,
'initially a string' => 51 ,
/neither string nor symbol/ => 52 ,
:'from default' => :default ,
}
@params1 = {
:name => 'Bill' ,
'alias' => 'Billy' ,
:'from params1' => arams1 ,
}
@params2 = {
'name' => 'Sam' ,
'alias' => 'Sammy' ,
12 => 'favourite number' ,
:'from params2' => arams2 ,
}
@default.extend SymbolizeKeys
end

must "retain new keys for merge" do
merged = @default.merge(@params2)
assert_equal merged[12] , 'favourite number'
end

must "retain new keys for merge!" do
@default.merge!(@params2)
assert_equal @default[12] , 'favourite number'
end

must "replace current values with new values for merge" do
merged = @default.merge(@params1)
assert_equal merged[:name] , 'Bill'
end

must "replace current values with new values for merge!" do
@default.merge!(@params1)
assert_equal @default[:name] , 'Bill'
end

must "not change original hash for merge" do
@default.merge(@params1)
assert_equal @default[:name] , 'Joe'
end

must "receive [key,oldval,newval] as params to block" do
h1 = {:no_conflict_1 => 1 , :conflict => 2 }.extend(SymbolizeKeys)
h2 = {:conflict => 3 , :no_conflict_2 => 4}
resolution_proc_params = nil
h1.merge(h2){ |*params| resolution_proc_params = params }
assert_equal resolution_proc_params , [:conflict,2,3]
end

must "replace resolve conflicts with block for merge" do
merged = @default.merge(@params1){ |key,oldval,newval| oldval }
assert_equal merged[:name] , 'Joe'

merged = @default.merge(@params1){ |key,oldval,newval| newval }
assert_equal merged[:name] , 'Bill'
end

must "replace resolve conflicts with block for merge!" do
@default.merge!(@params1){ |key,oldval,newval| oldval }
assert_equal @default[:name] , 'Joe'

@default.merge!(@params1){ |key,oldval,newval| newval }
assert_equal @default[:name] , 'Bill'
end

must "convert string keys to symbols for merge" do
merged = @default.merge(@params1)
assert_nil merged['alias']
assert_equal merged[ :alias] , 'Billy'
end

must "convert string keys to symbols for merge!" do
@default.merge!(@params1)
assert_nil @default['alias']
assert_equal @default[ :alias] , 'Billy'
end

must "merge with multiple hashes" do
merged = @default.merge(@params1,@params2)
assert_equal merged[:'from default'] , :default
assert_equal merged[:'from params1'] , arams1
assert_equal merged[:'from params2'] , arams2
end

must "merge! with multiple hashes" do
@default.merge!(@params1,@params2)
assert_equal @default[:'from default'] , :default
assert_equal @default[:'from params1'] , arams1
assert_equal @default[:'from params2'] , arams2
end

must "return object that is extended with SymbolizeKeys, for merge" do
merged = @default.merge(@params1)
assert_kind_of SymbolizeKeys , merged
end

must "not modify original hash, for merge" do
original = @default.dup
@default.merge(@params1,@params2)
assert_equal original , @default
end

end

 
Reply With Quote
 
Josh Cheek
Guest
Posts: n/a
 
      11-03-2009
[Note: parts of this message were removed to make it a legal post.]

On Fri, Oct 30, 2009 at 11:10 AM, shenry <> wrote:

> I'm trying to merge to hashes, one using symbols as keys (the defined
> default values for my class) and the other using strings as keys
> (taken from the params hash).
>
> default = { :name => "Joe", :age => 50 }
>
> params = { "name" => "Bill" }
>
> new_hash = default.merge(params)
> >> { :name => "Joe", :age => 50, "name" => "Bill }

>
> What's the Ruby way to handle this so that it overwrites :name with
> "name"? Do I need to implement a stringify_keys or symbolize_keys
> method like in Rails? I'd like to avoid using strings as the keys in
> my default hash.
>
> Any help greatly appreciated.
>
> Stu
>
>

I decided that I wasn't happy with the tests, it should be able to access
the same object through either a string or a symbol (previously it just
turned everything into a symbol, then if you tried to access that object w/
the string, it would not find it).

So I overrode [] and has_key? also, and changed some of the tests.

Here is the updated version



# file: symbolize_keys.rb

module SymbolizeKeys

# converts any current string keys to symbol keys
def self.extended(hash)
hash.each do |key,value|
if key.is_a?(String)
hash.delete key
hash[key] = value #through overridden []=
end
end
end

#considers string keys and symbol keys to be the same
def [](key)
key = convert_key(key)
super(key)
end

#considers string keys and symbol keys to be the same
def has_key?(key)
key = convert_key(key)
super(key)
end

# assigns a new key/value pair
# converts they key to a symbol if it is a string
def []=(*args)
args[0] = convert_key(args[0])
super
end

# returns new hash which is the merge of self and other hashes
# the returned hash will also be extended by SymbolizeKeys
def merge(*other_hashes , &resolution_proc )
merged = Hash.new.extend SymbolizeKeys
merged.merge! self , *other_hashes , &resolution_proc
end

# merges the other hashes into self
# if a proc is submitted , it's return will be the value for the key
def merge!( *other_hashes , &resolution_proc )

# default resolution: value of the other hash
resolution_proc ||= proc{ |key,oldval,newval| newval }

# merge each hash into self
other_hashes.each do |hash|
hash.each{ |k,v|
# assign new k/v into self, resolving conflicts with resolution_proc
self[k] = self.has_key?(k) ? resolution_proc[k.to_sym,self[k],v] : v
}
end

self
end

private

def convert_key(key)
key.is_a?(String) ? key.to_sym : key
end

end


--------------------------------------------------


# file: symbolize_keys_test.rb

require 'test/unit'
require 'symbolize_keys'

# this method was written by Gregory Brown
# and comes from
http://github.com/sandal/rbp/blob/7d..._extensions.rb
module Test::Unit
# Used to fix a minor minitest/unit incompatibility in flexmock
AssertionFailedError = Class.new(StandardError)

class TestCase

def self.must(name, &block)
test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym
defined = instance_method(test_name) rescue false
raise "#{test_name} is already defined in #{self}" if defined
if block_given?
define_method(test_name, &block)
else
define_method(test_name) do
flunk "No implementation provided for #{name}"
end
end
end

end
end


class ExtendingWithSymbolizeKeysTest < Test::Unit::TestCase
def setup
@default = {
:age => 50 ,
'initially a string' => 51 ,
/neither string nor symbol/ => 52 ,
}
@default.extend SymbolizeKeys
end

must "convert string keys to symbols when extended" do
assert_equal @default[:'initially a string'] , 51
end

must "sym/str keys can access through either, but only one key" do
assert_equal @default[ 'initially a string'] , 51
assert_equal @default[:'initially a string'] , 51
assert_equal @default[:'initially a string'] , @default['initially a
string']
assert_equal @default.size , 3
end

must "leave symbol keys as symbols" do
assert_equal @default[:age] , 50
end

must 'leave non symbols / strings as they are' do
assert_equal @default[/neither string nor symbol/] , 52
end

end


class SettingKeysWithSymbolizeKeysTest < Test::Unit::TestCase
def setup
@default = Hash.new.extend SymbolizeKeys
end

must "enable access to strings through symbols" do
@default['foo'] = 'bar'
assert_equal @default[:foo] , 'bar'
assert_same @default[:foo] , @default['foo']
end

must "enable access to symbols through strings" do
@default[:foo] = 'bar'
assert_equal @default['foo'] , 'bar'
assert_same @default[:foo] , @default['foo']
end

must 'leave non symbols / strings as they are' do
@default[/foo/] = :bar
assert_equal @default[/foo/] , :bar
end

end


class MergingWithSymbolizeKeysTest < Test::Unit::TestCase
def setup
@default = {
:name => 'Joe' ,
:age => 50 ,
'initially a string' => 51 ,
/neither string nor symbol/ => 52 ,
:'from default' => :default ,
}
@params1 = {
:name => 'Bill' ,
'alias' => 'Billy' ,
:'from params1' => arams1 ,
}
@params2 = {
'name' => 'Sam' ,
'alias' => 'Sammy' ,
12 => 'favourite number' ,
:'from params2' => arams2 ,
}
@default.extend SymbolizeKeys
end

must "retain new keys for merge" do
merged = @default.merge(@params2)
assert_equal merged[12] , 'favourite number'
end

must "retain new keys for merge!" do
@default.merge!(@params2)
assert_equal @default[12] , 'favourite number'
end

must "replace current values with new values for merge" do
merged = @default.merge(@params1)
assert_equal merged[:name] , 'Bill'
end

must "replace current values with new values for merge!" do
@default.merge!(@params1)
assert_equal @default[:name] , 'Bill'
end

must "not change original hash for merge" do
@default.merge(@params1)
assert_equal @default[:name] , 'Joe'
end

must "receive [key,oldval,newval] as params to block" do
h1 = {:no_conflict_1 => 1 , :conflict => 2 }.extend(SymbolizeKeys)
h2 = {:conflict => 3 , :no_conflict_2 => 4}
resolution_proc_params = nil
h1.merge(h2){ |*params| resolution_proc_params = params }
assert_equal resolution_proc_params , [:conflict,2,3]
end

must "only invoke the resolution proc on conflicts" do
conflict_count = 0
conflicts = { :name => false , :alias => false }
@params1.extend(SymbolizeKeys).merge(@params2) do |k,ov,nv|
conflict_count += 1
conflicts[k] = true
end
assert_equal conflict_count , 2
assert conflicts[:name]
assert conflicts[:alias]
end

must "replace resolve conflicts with block for merge" do
merged = @default.merge(@params1){ |key,oldval,newval| oldval }
assert_equal merged[:name] , 'Joe'

merged = @default.merge(@params1){ |key,oldval,newval| newval }
assert_equal merged[:name] , 'Bill'
end

must "replace resolve conflicts with block for merge!" do
@default.merge!(@params1){ |key,oldval,newval| oldval }
assert_equal @default[:name] , 'Joe'

@default.merge!(@params1){ |key,oldval,newval| newval }
assert_equal @default[:name] , 'Bill'
end

must "convert string keys to symbols for merge" do
unique_keys = @default.keys.map{|k|k.to_s.to_sym} |
@params1.keys.map{|k|k.to_s.to_sym}
merged = @default.merge(@params1)
assert_equal merged['alias'] , 'Billy'
assert_equal merged[ :alias] , 'Billy'
assert_equal merged.size , unique_keys.size
end

must "convert string keys to symbols for merge!" do
unique_keys = @default.keys.map{|k|k.to_s.to_sym} |
@params1.keys.map{|k|k.to_s.to_sym}
@default.merge!(@params1)
assert_equal @default['alias'] , 'Billy'
assert_equal @default[ :alias] , 'Billy'
assert_equal @default.size , unique_keys.size
end

must "merge with multiple hashes" do
merged = @default.merge(@params1,@params2)
assert_equal merged[:'from default'] , :default
assert_equal merged[:'from params1'] , arams1
assert_equal merged[:'from params2'] , arams2
end

must "merge! with multiple hashes" do
@default.merge!(@params1,@params2)
assert_equal @default[:'from default'] , :default
assert_equal @default[:'from params1'] , arams1
assert_equal @default[:'from params2'] , arams2
end

must "return object that is extended with SymbolizeKeys, for merge" do
merged = @default.merge(@params1)
assert_kind_of SymbolizeKeys , merged
end

must "not modify original hash, for merge" do
original = @default.dup
@default.merge(@params1,@params2)
assert_equal original , @default
end

end

 
Reply With Quote
 
Robert Klemme
Guest
Posts: n/a
 
      11-03-2009
2009/11/3 Josh Cheek <>:

> Here is the updated version


Josh, I believe it's better to create a gist for code of that length
and paste the link only. That makes it easier to follow - especially
since you'll get code highlighting and versioning for free.

Kind regards

robert

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

 
Reply With Quote
 
Josh Cheek
Guest
Posts: n/a
 
      11-03-2009
[Note: parts of this message were removed to make it a legal post.]

On Tue, Nov 3, 2009 at 7:06 AM, Robert Klemme <>wrote:

> 2009/11/3 Josh Cheek <>:
>
> > Here is the updated version

>
> Josh, I believe it's better to create a gist for code of that length
> and paste the link only. That makes it easier to follow - especially
> since you'll get code highlighting and versioning for free.
>
> Kind regards
>
> robert
>
> --
> remember.guy do |as, often| as.you_can - without end
> http://blog.rubybestpractices.com/
>
>

Hi, Robert, I always thought that too, but I was told I should put them in
the email in case the host site went down (
http://www.ruby-forum.com/topic/192987#841723 )

Is there some standard that I can follow, because there seems to be
conflicts of opinion regarding the best approach.

 
Reply With Quote
 
Paul Smith
Guest
Posts: n/a
 
      11-03-2009
On Tue, Nov 3, 2009 at 1:22 PM, Josh Cheek <> wrote:
> On Tue, Nov 3, 2009 at 7:06 AM, Robert Klemme <=
>wrote:
>
>> 2009/11/3 Josh Cheek <>:
>>
>> > Here is the updated version

>>
>> Josh, I believe it's better to create a gist for code of that length
>> and paste the link only. =A0That makes it easier to follow - especially
>> since you'll get code highlighting and versioning for free.
>>
>> Kind regards
>>
>> robert
>>
>> --
>> remember.guy do |as, often| as.you_can - without end
>> http://blog.rubybestpractices.com/
>>
>>

> Hi, Robert, I always thought that too, but I was told I should put them i=

n
> the email in case the host site went down (
> http://www.ruby-forum.com/topic/192987#841723 )
>
> Is there some standard that I can follow, because there seems to be
> conflicts of opinion regarding the best approach.
>


A big difference here is that a pastie is only available through
pastie.org, a gist is a git repository that can be cloned.

--=20
Paul Smith
http://www.nomadicfun.co.uk



 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Merging hashes and having trouble with variable scope! Andy Pipes Ruby 6 09-30-2008 02:43 PM
non-destructive merging of hashes in array Giles Bowkett Ruby 13 03-15-2007 04:22 PM
Merging potentially undefined hashes Derek Basch Perl Misc 15 06-13-2006 01:40 PM
using hashes as keys in hashes Steven Arnold Ruby 3 11-23-2005 03:25 PM
Hash of hashes, of hashes, of arrays of hashes Tim O'Donovan Perl Misc 5 10-28-2005 05:59 AM



Advertisments
 



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57