Hash :: Recursive traversing and stripping the values


#1

Hi,
I am a Ruby N…

I wrote a function for stripping all the values(a recursive one if

it is a nested hash) but i almost end up in an
error suggesting “Error => in `strip_hash_values’: stack level too
deep (SystemStackError)”.
Can someone help me with this.

-------------------------------------------
Here's the code:
-------------------------------------------

def strip_hash_values(hash)

  hash.each do |k, v|

    if v.class == String
      hash[k] == v.strip!

    elsif v.class == Array
      v.each do |vv|
        vv.strip!
      end

    elsif v.class == Hash
      strip_hash_values(hash)
    end

  end

end

Thanx,
Vimal Das


#2

vimal removed_email_address@domain.invalid writes:

Hi,
I am a Ruby N…

I wrote a function for stripping all the values(a recursive one if

it is a nested hash) but i almost end up in an
error suggesting “Error => in `strip_hash_values’: stack level too
deep (SystemStackError)”.
Can someone help me with this.

Perhaps a circular data structure is the problem:

irb(main):001:0> h = Hash.new
{}
irb(main):002:0> h[:ha]=Hash.new
{}
irb(main):003:0> h[:ha][:ho]=h
{:ha=>{:ho=>{…}}}
irb(main):004:0> h
{:ha=>{:ho=>{…}}}
irb(main):005:0> h[:ha][:ho][:ha]
{:ho=>{:ha=>{…}}}
irb(main):006:0> h[:ha][:ho][:ha][:ho]
{:ha=>{:ho=>{…}}}
irb(main):007:0>


#3

So is there any other way to carry out this stripping
functionality thoughout the hash(if nested too)!!!


#4

On Mon, Feb 16, 2009 at 5:50 PM, vimal removed_email_address@domain.invalid
wrote:

error suggesting “Error => in `strip_hash_values’: stack level too
deep (SystemStackError)”.

Vimal, take a very close look at the recursive call:


elsif v.class == Hash
strip_hash_values(hash)
end

You have a simple typo/bug there.

When i free up a couple of minutes i’ll follow up with some additional
observations.

HTH for now,
lasitha


#5

On Mon, Feb 16, 2009 at 6:20 PM, lasitha removed_email_address@domain.invalid
wrote:

   end

You have a simple typo/bug there.

Some further observations:

  1. This line uses a comparison operator, not the assignment operator:
    hash[k] == v.strip!

  2. We’re getting away with the above typo because we’re using #strip!
    (the version with side-effect). So the string v is being modified in
    place and therefore doesn’t need to be assigned back to hash[k].
    Needless to say this is redundant - we should either use #strip or get
    rid of the assignment.

  3. A case statement may be a better choice than if/else. I tend to
    consider a case statement as soon as i have more than two branches -
    it reads slightly cleaner and is easier to add new cases to. See the
    example below.

  4. I would consider adding a catch-all clause and failing fast with
    an exception if the hash contains an unhandled type. Even if you
    later choose to silently ignore unhandled types, keeping the else
    clause in the code usefully documents your intent. Again, see the
    example below.

The code below includes rspec (test) code in case that’s of
use/inspiration.

Cheers,
lasitha.

The following code can be also be seen at:
http://pastie.org/390656

def strip_hash_values(hash)

hash.each_value do |v|
case v
when String then v.strip!
when Array then v.each {|i| i.strip! }
when Hash then strip_hash_values(v)
else raise ArgumentError, “Unhandled type #{v.class}”
end
end

end

describe ‘strip_hash_values’ do

it ‘should recurse and strip hash values’ do
hash = { c: ’ s ‘, a: [’ an ', ’ array '], h: { nested: ’ hash ’
} }
stripped = { c: ‘s’, a: [ ‘an’, ‘array’ ], h: { nested: ‘hash’
} }
strip_hash_values(hash).should eql(stripped)
end

it ‘should raise an exception if the hash contains an unhandled type’
do
lambda { strip_hash_values( { e: 1 } ) }.should
raise_error(ArgumentError)
end

end


#6

On Feb 16, 7:03 pm, “Alexandru E. Ungur”
removed_email_address@domain.invalid wrote:

should actually be:

    elsif v.class ==Hash
      strip_hash_values(v)
    end

Additionally, you could ‘cheat’ and use “case” (no more need to check
the type/class manually):

This is what an user expects to learn(some additional tips) and
master a scripting lang

    end

end
end

Cheers,
Alex

Thanks for all your suggestions
I now figure it how.


#7

sender: “vimal” date: “Mon, Feb 16, 2009 at 09:40:02PM +0900” <<<EOQ
So is there any other way to carry out this stripping
functionality thoughout the hash(if nested too)!!!

EOQ
Of course there is:

    elsif v.class == Hash
      strip_hash_values(hash)
     end

should actually be:

    elsif v.class == Hash
      strip_hash_values(v)
     end

Additionally, you could ‘cheat’ and use “case” (no more need to check
the type/class manually):

def strip_hash_values(hash)
hash.each do |k, v|
case v
when String
v.strip!
when Array
v.each {|vv| vv.strip!}
when Hash
strip_hash_values(v)
end
end
end

Cheers,
Alex


#8

Hi Guys,

I found out another way to do this using a ‘lambda’ function
I am just curious to know, how effective is this compared to the
above mentioned suggestions


Code modified from source
http://blog.hasmanythrough.com/2008/6/20/recursive-lambda


thunk = lambda do |key,value|
case value
when String then value.strip!
when Hash then value.each(&thunk)
when Array then value.each {|vv| vv.strip!}
end
end


Call

.each(&thunk)

Thanks,
Vimal Das
Waiting with curiosity :expressionless: