Velocity Reviews

Velocity Reviews (http://www.velocityreviews.com/forums/index.php)
-   Ruby (http://www.velocityreviews.com/forums/f66-ruby.html)
-   -   count : array to array of hashes (http://www.velocityreviews.com/forums/t842925-count-array-to-array-of-hashes.html)

Jean-Sébastien 08-01-2007 04:01 PM

count : array to array of hashes
 
Hi,
I'm tryind to count distinct ids from an array (array_of_ids =
[1,2,3,1,2]), and want a result array of hashes looking like it :
result = [{;id=>1,:count=>2},{;id=>2,:count=>2},{;id=>3,:cou nt=>1}]

i tried it, but it always returns errors :
result = array_of_ids.inject(Array.new){ |a,x| elt = a.find{|h| h[:id]
== x} || {:id => x, :count => 0}; elt[:count] += 1; elt}

i took my information from this post :
http://groups.google.com/group/comp....a7b10da8195cec

If someone could help.

regards


Robert Dober 08-01-2007 04:33 PM

Re: count : array to array of hashes
 
On 8/1/07, Jean-S=E9bastien <jeansebastien.ney@gmail.com> wrote:
> Hi,
> I'm tryind to count distinct ids from an array (array_of_ids =3D
> [1,2,3,1,2]), and want a result array of hashes looking like it :
> result =3D [{;id=3D>1,:count=3D>2},{;id=3D>2,:count=3D>2},{;id =3D>3,:coun=

t=3D>1}]
>
> i tried it, but it always returns errors :
> result =3D array_of_ids.inject(Array.new){ |a,x| elt =3D a.find{|h| h[:id=

]
> =3D=3D x} || {:id =3D> x, :count =3D> 0}; elt[:count] +=3D 1; elt}
>
> i took my information from this post :
> http://groups.google.com/group/comp....thread/312221=

2f6db8d7f5/04a7b10da8195cec?lnk=3Dgst&q=3Darray++count&rnum=3 D8#04a7b10da81=
95cec
>
> If someone could help.
>
> regards
>
>
>

irb(main):005:0> a=3D[1,2,3,42,2,2,3]
=3D> [1, 2, 3, 42, 2, 2, 3]
irb(main):006:0> a.inject([]){|r,ele| r[ele][:count]+=3D1 rescue
r[ele]=3D{:id =3D> ele, :count=3D>1}; r}.compact
=3D> [{:count=3D>1, :id=3D>1}, {:count=3D>3, :id=3D>2}, {:count=3D>2, :id=
=3D>3},
{:count=3D>1, :id=3D>42}]
irb(main):007:0>

HTH
Robert



--=20
[...] as simple as possible, but no simpler.
-- Attributed to Albert Einstein


Stefano Crocco 08-01-2007 04:55 PM

Re: count : array to array of hashes
 
Alle mercoled=EC 1 agosto 2007, Jean-S=E9bastien ha scritto:
> Hi,
> I'm tryind to count distinct ids from an array (array_of_ids =3D
> [1,2,3,1,2]), and want a result array of hashes looking like it :
> result =3D [{;id=3D>1,:count=3D>2},{;id=3D>2,:count=3D>2},{;id =3D>3,:coun=

t=3D>1}]
>
> i tried it, but it always returns errors :
> result =3D array_of_ids.inject(Array.new){ |a,x| elt =3D a.find{|h| h[:id]
> =3D=3D x} || {:id =3D> x, :count =3D> 0}; elt[:count] +=3D 1; elt}
>
> i took my information from this post :
> http://groups.google.com/group/comp....thread/312221=

2f
>6db8d7f5/04a7b10da8195cec?lnk=3Dgst&q=3Darray++count&rnum=3 D8#04a7b10da819=

5cec
>
> If someone could help.
>
> regards


There are two problems in your code:
1- in the inject block, you return elt, which is (or should be, if the code=
=20
worked) the hash containing the id which is being processed. You should=20
return a, that is the array which contains the hashes. Correcting this shou=
ld=20
give a piece of code which executes without errors, but which returns an=20
empty array
2- you never insert the hashes you create inside the inject block into the=
=20
array a: you only store them in the local variable elt, which gets destroye=
d=20
after each iteration. The inject block should be:

result =3D array_of_ids.inject(Array.new){ |a,x|=20
elt =3D ( a.find{|h| h[:id] =3D=3D x} || a[a.size] =3D {:id =3D> x, :co=
unt =3D> 0} )
elt[:count] +=3D 1
a
}

As you can see, after the || operator a new hash is created and inserted at=
=20
the end of a (corresponding to the index a.size). Since an assignment alway=
s=20
return the value being assigned (this is why I didn't use <<, it returns th=
e=20
array, not the inserted element), elt is then set to the new hash. Of cours=
e,=20
all this happens only if find returns nil.

If you can rely in the id to be positive integers, and don't care if the=20
resulting array contains the hashes in the same order as the id are stored =
in=20
the array, here's another approach you can consider:

result =3D array_of_ids.inject([]) do |res, i|
res[i-1] ||=3D {:id =3D> i, :count =3D> 0}
res[i-1][:count] +=3D 1
res
end

This code stores the data relative to the id i in the i-1 position in the=20
array (the -1 is there to avoid a nil element at the beginning). This shoul=
d=20
make it faster, since you don't need to iterate all the array to check=20
whether the data corresponding to an id is already there or not: either is =
in=20
the position id - 1 or it itsn't there. A quick benchmark, done by creating=
=20
an array of ids of 100_000 elements, with values randomly chosen between 1=
=20
and 11 gives:

user system total real
original approach 5.730000 1.490000 7.220000 ( 7.310224)
user system total real
alternative approach 1.250000 0.150000 1.400000 ( 1.416871)

Changing the range of the ids from 11 to 101 gives:
user system total real
alternative approach 1.270000 0.190000 1.460000 ( 1.472353)
user system total real
original approach 37.730000 11.360000 49.090000 ( 51.056527)

Increasing it to 1001 gives
user system total real
alternative approach 1.500000 0.220000 1.720000 ( 1.733568)

The original approach takes much more time (I didn't have the patience to w=
ait=20
for it to complete).

I hope this helps

Stefano




All times are GMT. The time now is 12:44 AM.

Powered by vBulletin®. Copyright ©2000 - 2014, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.