# Groovy script to get average price per group from different data arrays

I've been trying to learn Groovy lately and I tried to solve a problem which involves designing a routine that will calculate the average Product price per Group.

I have the following data:

// contains information about [Product, Group, Cost]
products = [
["A", "G1", 20.1],
["B", "G2", 98.4],
["C", "G1", 49.7],
["D", "G3", 35.8],
["E", "G3", 105.5],
["F", "G1", 55.2],
["G", "G1", 12.7],
["H", "G3", 88.6],
["I", "G1", 5.2],
["J", "G2", 72.4]
]

// contains information about Category classification based on product Cost
// [Category, Cost range from (inclusive), Cost range to (exclusive)]
// i.e. if a Product has Cost between 0 and 25, it belongs to category C1
category = [
["C3", 50, 75],
["C4", 75, 100],
["C2", 25, 50],
["C5", 100, null],
["C1", 0, 25]
]

// contains information about margins for each product Category
// [Category, Margin (either percentage or absolute value)]
margins = [
"C1" : "20%",
"C2" : "30%",
"C3" : "0.4",
"C4" : "50%",
"C5" : "0.6"]


What I tried so far is the following (I mixed some Java here):

def calculateResult() {
def group1 = []
def group2 = []
def group3 = []
def pricePerGroupMap = [:]

category.each { cat ->
String catDesc = cat[0]
BigDecimal min = cat[1]
BigDecimal max = cat[2]

if (max == null) {
max = 9999.9;
}

products.each { product ->
BigDecimal currProductPrice = BigDecimal.valueOf(product[2])
if (currProductPrice.compareTo(min) >= 0 && currProductPrice.compareTo(max) <= 0) {
String selectedMarginAsString = margins.getAt(catDesc);
BigDecimal selMargin = 0.0;

if (selectedMarginAsString.endsWith("%")) {
selMargin = new BigDecimal(selectedMarginAsString.trim().replace("%", "")).divide(BigDecimal.valueOf(100));
} else {
selMargin = new BigDecimal(selectedMarginAsString);
}

if (product[1].equals("G1")) {
} else if (product[1].equals("G2")) {
} else if (product[1].equals("G3")) {
}
}

}
}
pricePerGroupMap["G1"] = group1.sum() / group1.size()
pricePerGroupMap["G2"] = group2.sum()/ group2.size()
pricePerGroupMap["G3"] = group3.sum()/ group3.size()
print pricePerGroupMap
}


I am sure there are better ways to optimize this algorithm taking advantage of closures.

After being investigating a little bit more about usage of closures and some other facilities that Groovy provides, I managed to reduce a little bit the algorithm, after looking for benchmarking posts, I believe this is probably not the most optimal solution, but it looks a lot better than the first proposal (the one in my question).

One of the things I considered to improve the solution is to use Groovy "maps" instead of multiple arrays to hold the data.

I also considered using one additional variable to hold the value of a group being evaluated.

And finally, I decided to avoid using more conditionals to add the results to the arrays and use a more simple instruction taking advantage of the map definition.

MAX_DEFAULT_VALUE_WHEN_NULL = 99999.9

def calculateResult() {
def pricesPerGroupMap = [G1:[], G2:[], G3:[]]
def avgPricePerGroupMap = [:]

category.each { cat ->
String categoryId = cat[0]
BigDecimal min = cat[1]
BigDecimal max = cat[2]

if (max == null) {
max = MAX_DEFAULT_VALUE_WHEN_NULL
}

products.each { product ->
BigDecimal currProductPrice = BigDecimal.valueOf(product[2])
String group = product[1]
if (currProductPrice.compareTo(min) >= 0 && currProductPrice.compareTo(max) == -1) {
String selectedMarginAsString = margins.getAt(categoryId);
BigDecimal selMargin = 0.0;

if (selectedMarginAsString.endsWith("%")) {
selMargin = new BigDecimal(selectedMarginAsString.trim().replace("%", "")).divide(BigDecimal.valueOf(100))
} else {
selMargin = new BigDecimal(selectedMarginAsString)
}