How to make Groovy / Grails return a list of objects instead of a list of lists of objects?

I have a class like this:

class Foo { static hasMany = [bars: Bar] } 

When I write:

 Foo.getAll() 

I get a list of Foo objects like this:

 [ Foo1, Foo2, Foo3 ] 

When I write:

 Foo.getAll().bars 

I get a list of lists of the Bar object as follows:

 [ [ Bar1, Bar2 ], [ Bar2, Bar3 ], [ Bar1, Bar4 ] ] 

But I want this to be a unique list of Bar objects, such as:

 [ Bar1, Bar2, Bar3, Bar4 ] 

My ultimate goal is to have a unique list of Bar object identifiers in the list above, for example:

 [ 1, 2, 3, 4 ] 

I tried the variations of the collect method, and I also tried the distribution operator , but I had no luck.

+6
source share
3 answers

For generic groovy classes (i.e. classes other than GORM), David is right that flatten and unique best.

In your example, however, it looks like you are using GORM domain objects in a many-to-many relationship (otherwise you will not need a unique constraint).

For a domain class, you are better off using either HQL or criteria to do this in one step. An added benefit is that the SQL generated for it is much more efficient.

Here's the HQL for getting unique Bar identifiers that are associated with any Foo many-to-many relationship:

 Bar.executeQuery("select distinct b.id from Foo f join f.bars b") 

The criteria for this will look like this:

 Foo.withCriteria { bars { projections { distinct("id") } } } 

One β€œproblem” using this method is that HQL is not supported in unit tests (and probably never will be), and Query Criteria with connection table projections are broken down into 2.0.4 unit tests . Thus, any tests around this code would either have to mock or use an integration test.

+8
source

There may be a better way, but you can use flatten and unique .

Sort of:

def allBars = Foo.getAll().bars.flatten().unique()

Edit:
I just re-read and realized that you are after a list of identifiers, not bars. As you said, you would use collect{ it.id } to get identifiers. I would do this before making the list unique for performance reasons.

+4
source

There is a Collection#collectMany method that works just like calling collect and then flatten . To get a list of Bar identifiers, you can:

 def barIds = Foo.getAll().collectMany { it.bars*.id } 

Closing { it.bars*.id } returns a list with the identifiers of the bars Foo (I love these hehe placeholder names), and then collectMany combines these lists into a single list. If you want the identifiers to be unique (perhaps Bar can belong to more than one Foo), you can add .unique() to this expression :)

+4
source

Source: https://habr.com/ru/post/918614/


All Articles