Reputation: 121
I'm developing a project that would read certain data in proprietary format, gather it into some unified Groovy data structure, and write it out in a neat XML (or, JSON, haven't decided yet) format. I'm a total newbie to Groovy, but, the project being Ant build project, Groovy seems the best way to go.
So far so good, I'm able to read data, create atomic structures, concatenate them using >> operator, and seamlessly dump them to XML with MarkupBuilder (ways easier than if I were doing it in Java). However, I'm stuck now at the point when I need to slightly modify the gathered structures, or traverse through them to compose some aggregated data.
To illustrate, supposing we collected our data so it's equivalent to:
def inventory = {
car (make: "Subaru", model: "Impreza WRX", year: 2010, color: "Blue") {
feature ("Premium sound")
feature ("Brembo brakes")
bug ("Leaks oil")
bug ("Needs new transmission")
}
car (make: "Jeep", model: "Wrangler", year: 13, awd: true) {
feature ("Soft top")
bug ("Doesn't start")
bug ("Flooded")
}
// blahblahblah
}
and we're trying to achieve following, for example:
so we end up with a structure like this:
def inventory = {
car (make: "Subaru", model: "Impreza WRX", year: 2010, color: "Blue") {
feature ("Premium sound")
feature ("Brembo brakes")
}
car (make: "Jeep", model: "Wrangler", year: 2013) {
feature ("AWD")
feature ("Soft top")
}
// blahblahblah
}
Actually, I'm OK with going through the original structure composing a new list (my data isn't that huge to require in-place editing), but how do I traverse through this structure, in the first place?
Oh, and a question of terminology. Maybe, I was just googling around for a wrong keyword... This entity as defined in code: is it called "closure" too, or there's a different term for it?
Upvotes: 1
Views: 696
Reputation: 12998
Looking at inventory
, it could actually being represented by something like
@Immutable(copyWith = true)
class Car {
String make
String model
int year
String color
boolean awd
List<String> features
List<String> bugs
}
As you are fine with composing a new list (and there are generally speaking few reasons not to do so), you can add the @Immutable
annotation. As a "side effect" you can add copyWith = true
to get a copy method.
The definition of the inventory
could look like this:
def inventory = [
new Car(
make: "Subaru",
model: "Impreza WRX",
year: 2010,
color: "Blue",
features: ["Premium sound", "Brembo brakes"],
bugs: ["Leaks oil", "Needs new transmission"]),
new Car(
make: "Jeep",
model: "Wrangler",
year: 13,
awd: true,
features: ["Soft top"],
bugs: ["Doesn't start", "Flooded"])
]
which gives you an immutable represenation of your data. Then you can traverse and change your data using the collection API. In your case:
def result = inventory
.collect { it.copyWith(bugs: []) }
.collect { it.copyWith(year:
it.year < 2000 ? it.year + 2000 : it.year) }
.collect {
if (it.awd) {
it.copyWith(features: it.features + "AWD")
} else {
it
}
Each transformation is applied individually (and there are only immutable instances flowing through that pipeline).
If you really want to remove the the empty bugs list and/or the awd
property, define the target class:
class FixedCar {
String make
String model
int year
String color
List<String> features
static FixedCar apply(Car car) {
new FixedCar(
make: car.make,
model: car.model,
year: car.year,
color: car.color,
features: car.features)
}
}
and add another call to collect
:
def result = inventory
.collect { it.copyWith(bugs: []) }
.collect { it.copyWith(year:
it.year < 2000 ? it.year + 2000 : it.year) }
.collect {
if (it.awd) {
it.copyWith(features: it.features + "AWD")
} else {
it
}
}.collect { FixedCar.apply(it) }
Upvotes: 1