Forum: Ruby habtm on an array

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Daniel (Guest)
on 2006-05-31 15:28
Hi,

I have three Models

User, Group and File.

User habtm group
Group habtm File

How can I find all that files that a particular user has access to?
eg user.groups.files ?
Matthew S. (Guest)
on 2006-05-31 15:40
(Received via mailing list)
On May 31, 2006, at 12:28, Daniel wrote:

> eg user.groups.files ?
The Rails-specific list (http://lists.rubyonrails.org/mailman/
listinfo/rails) and wiki and things may give you a better answer
(particularly in terms of things like query optimisation) than you'll
get here, but...

user.groups is an array, it's not the array which has a file
attribute, but each individual group in it, so maybe something like
this:

class User
   def files
     self.groups.inject([]) { |arr, group| arr + group.files }
   end
end

matt smillie.
Kirk H. (Guest)
on 2006-05-31 16:21
(Received via mailing list)
On Wednesday 31 May 2006 5:28 am, Daniel wrote:

> I have three Models
>
> User, Group and File.
>
> User habtm group
> Group habtm File
>
> How can I find all that files that a particular user has access to?
> eg user.groups.files ?

Step back from the problem a step.  Forget that you are working with
models or
that you are dealing with habtm relationships in Rails.

user.groups returns an array.  And each of the elements in that array,
if you
call files() on it, returns another array.

So all you are really looking at here is a requirement to iterate over
an
array of arrays, creating a set consisting of each of the unique values
from
those arrays.  Strip all the Rails stuff away, and it's just a simple
data
problem.

So, create a simple surrogate to think about:

a = [[1,2,3],
  [2,3,4],
  [3,4,5],
  [4,5,6],
  [5,6,7],
  [6,7,8]]

That's just an array of arrays.  Arrays in Ruby provide some handy
methods for
treating them like sets.  And what you want is the union of all of your
sets.
With an array, | performs a union operation.  So:

[1.,2,3] | [2,3,4]

returns

[1,2,3,4]

So all that you need to do now is iterate through your array applying
each sub
array to a common array with the union operator.

arr = []
a.each {|elem| arr = arr | elem}

That works.  You get:

[1,2,3,4,5,6,7,8]

FYI, Ruby also provides a method, inject(), that is perfect for this
kind of
operation.  So, the above code can be rewritten like so:

a.inject([]) {|arr,elem| arr | elem}

inject() just takes a starting value and a block.  The arguments to the
block
are an accumulator variable, which is initialized to the starting value
passed to inject, and each successive element of the array inject it
iterating over.  The code in the block is evaluated and the result of
the
code is inserted into the accumulator variable.  When all values of the
array
have been iterated over, inject() returns the final value of the
accumulator.
It's the loop for code that needs to accumulate values.

Anyway, that's the way to solve the problem with an arbitrary set of
arrays.
So, now just apply it to your model.

Something like this, probably:

class User
  def files
    groups.inject([]) {|all_files, some_files|  all_files | some_files}
  end
end

Hope this helps,


Kirk H.
Kroeger, Simon (ext) (Guest)
on 2006-05-31 17:26
(Received via mailing list)
> -----Original Message-----
> From: Kirk H. [mailto:removed_email_address@domain.invalid]

> [...nice breakdown...]

> a.inject([]) {|arr,elem| arr | elem}

a.flatten.uniq ?

> [...]
> Something like this, probably:
>
> class User
>   def files
>     groups.inject([]) {|all_files, some_files|  all_files |
> some_files}
>   end
> end

Should be equivalent to:

groups.flatten.uniq

But does probably not work because (as you explained)

"...And each of the elements in that array, if you
call files() on it, returns another array"

class User
  def files
    groups.inject([]) {|all_files, group|  all_files | group.files}
  end
end

> Hope this helps,
>
>
> Kirk H.

cheers

Simon
Kirk H. (Guest)
on 2006-05-31 17:35
(Received via mailing list)
On Wednesday 31 May 2006 7:23 am, Kroeger, Simon (ext) wrote:

> Should be equivalent to:
>     groups.inject([]) {|all_files, group|  all_files | group.files}
>   end
> end

Doh!  Yeah.  I goofed.  Thanks.

Kirk H.
Kroeger, Simon (ext) (Guest)
on 2006-05-31 17:50
(Received via mailing list)
> > class User
> >   def files
> >     groups.inject([]) {|all_files, group|  all_files | group.files}
> >   end
> > end
>
> Doh!  Yeah.  I goofed.  Thanks.
>
> Kirk H.

We all do that - from time to time :)

As Rails implements Symbol#to_proc one should be able to do

myuser.groups.map(&:files).flatten.uniq

but that is getting esotheric.

cheers

Simon
This topic is locked and can not be replied to.