"Meino Christian Cramer" <Meino.Cramer@gmx.de> schrieb im Newsbeitrag
news:20040817.105109.41634481.Meino.Cramer@gmx.de...
From: "Robert Klemme" <bob.news@gmx.net>
Subject: Re: Q: Shifting a hash anf and array
Date: Tue, 17 Aug 2004 17:21:01 +0900
Hi Martin, Hi Robert,
thank you very much for your sources !
Great! 
You're welcome!
I have an additional question below.....vvvvvv
Ok, answers below.
>
> "Martin DeMello" <martindemello@yahoo.com> schrieb im Newsbeitrag
> news:u_gUc.122944$M95.4319@pd7tw1no...
> > >
> > > Contents of the array before the trick:
> > >
> > > 1 2 3 4 5 6 7 8 9 0 index
> > > A B C D E F G H I J contents
> > >
> > > (execute *trick*)
> > >
> > > Contents of the array after the trick:
> > >
> > > 1 2 3 4 5 6 7 8 9 0 index
> > > J A B C D E F G H I contents
> >
> > array.unshift(array.pop)
>
> or array.push(array.shift) for the other direction.
>
> > > And now the shift of an hash:
> > >
> > > Contents of the hash before the trick:
> > >
> > > A B C D E F G H I J key
> > > 1 2 3 4 5 6 7 8 9 0 value
> > >
> > > (execute *trick*)
> > >
> > > Contents of the hash after the trick:
> > >
> > > A B C D E F G H I J key
> > > 0 1 2 3 4 5 6 7 8 9 value
> > >
> > > Both are very similiar.
> >
> > Not really
Hashes are inherently unordered, so the concept of a
> > rotation is a bit trickier. This works on the assumption that
> > Hash#values returns values in positions corresponding to the keys in
> > Hash#keys...
> >
> > class Array
> > def rot1
> > unshift(pop)
> > end
> > end
> >
> > Hash[*(hash.keys.rot1.zip(hash.values.rot1).flatten)]
>
> Hm, IMHO there is one "rot1" too much: you're rotating keys *and* values
> while you should only rotate one of them. Otherwise I figure the result
> will be the same as the original hash... 
>
> The other drawback of your solution is that it relys on #keys and
#values
> returning elements in matching order, i.e., if you zip both you get the
same
> hash. Although that might work it makes me feel a bit wary because
AFAIK
> it's not a guaranteed property of Hash (and I wouldn't want to rely on
it).
> This might even change over time if the Hash implementation was changed
for
> any reasons.
>
> How about these one liners, that avoids the problem:
>
> h = {0=>"a", 1=>"b", 2=>"c"}
> # put rotated in h2
> (h2={}).send(:=, *h.sort.inject {|(k1,v1),(k2,v2)| h2[k1]=v2; [k2,
v1]})
I tried to understand this...
"h = {0=>"a", 1=>"b", 2=>"c"}" generates an hash...ok
"(h2={}).send(:=" generates h2, another hash and sends to its =
(element assignment) the....
"*h.sort.inject {|(k1,v1),(k2,v2)| h2[k1]=v2; [k2, v1]})"
...?... I stumbled over the "*" at *h.sort.....What does it mean?
If you execute h.sort.inject {|(k1,v1),(k2,v2)| h2[k1]=v2; [k2, v1]} you'll
see the result is an array:
h.sort.inject {|(k1,v1),(k2,v2)| h2[k1]=v2; [k2, v1]}
=> [2, "a"]
You need unary "*" to make this an argument list for the invocation of =.
Some example usage of "*" for clarification (hopefully):
def foo(*args) p args end
=> nil
foo 1
[1]
=> nil
foo [1]
[[1]]
=> nil
foo(1,2,3)
[1, 2, 3]
=> nil
foo([1,2,3])
[[1, 2, 3]]
=> nil
> # rotate inplace
> h.send(:=, *h.sort.inject {|(k1,v1),(k2,v2)| h[k1]=v2; [k2, v1]})
...same here...
> 
>
> Note: you can omit the sort in the first example, but IMHO an order
should
> be imposed on keys. Otherwise the rotation does not make much sense
IMHO.
Absolutely correct. I was searching for something like "an array
with indices not being integers but ordered".
> You can omit the sort in the second example, too, which seems to work
> because the set of keys of the hash doesn't change. But this seems a
bit
> hackish to me, too. You can substitute "sort" by "dup" though.
>
> Kind regards
>
> robert
>
>
> PS: Enumerable#inject is cool. 
PS: Undertstanding and writing such code is much cooler... !
:-))
#inject works like this: the block receives two values, the result of the
last block execution and the current iteration element. The result of
inject is the value of the last block evaluation. At first invocation the
value of the last execution is either the value provided as argument to
inject if there is one or otherwise the first element of the iteration and
the current element is the second element:
%w{a b c d}.inject("foo") {|*args| p args; args.join('|')}
["foo", "a"]
["foo|a", "b"]
["foo|a|b", "c"]
["foo|a|b|c", "d"]
=> "foo|a|b|c|d"
%w{a b c d}.inject {|*args| p args; args.join('|')}
["a", "b"]
["a|b", "c"]
["a|b|c", "d"]
=> "a|b|c|d"
You can do all sorts of cool things with inject, in fact you can implement
all Enumerable methods based on inject - although that's not always the most
elegant or efficient method. Counting is probably the easiest:
module Enumerable
def count
inject(0) {|cnt,| cnt + 1}
end
end
%w{a b c d}.count
=> 4
But other algorithms work, too:
module Enumerable
def find_elem
inject() {|sel, item| sel << item if yield item; sel}
end
end
%w{a b c d}.find_elem {|e| /^a/ =~ e}
=> ["a"]
Another feature I used was bracketing of block arguments to assign keys and
values to individual variables. During a Hash iteration with inject, the
current element is a two element array with key and value:
{1=>"a",2=>"b",3=>"c"}.inject {|*args| p args}
[[1, "a"], [2, "b"]]
[nil, [3, "c"]]
=> nil
By using brackets you can control assignment of array elemtens to variables:
test = lambda {|(a,b),c,*d| p a, b, c, d}
=> #<Proc:0x10180b30@(irb):18>
test.call [1,2],3,4,5,6,7,8
1
2
3
[4, 5, 6, 7, 8]
=> nil
test = lambda {|a,(b,*c),*d| p a, b, c, d}
=> #<Proc:0x101a0108@(irb):23>
test.call 1,[2,3,4],5,6,7,8,9
1
2
[3, 4]
[5, 6, 7, 8, 9]
=> nil
In my code the first value is carried through the whole iteration while the
key of the last value is always the last key, which is assigned the current
value.
I hope that made things a bit clearer. 
Ruby.use!
Definitely!
Kind regards
robert
···
> > Meino Christian Cramer <Meino.Cramer@gmx.de> wrote: