Science and technology

Managing a non-profit group’s provide chain with Groovy

There are many causes I am an enormous fan of Java, however maybe most of all, due to the actual combo of static typing and object-orientedness that imbues its design. However, after I want a fast resolution, particularly to a “solve it and forget it” downside coping with knowledge, I normally attain for Groovy (or typically Python) as a substitute, particularly if the library that addresses my downside exists and is well-documented. Sometimes even awk will do. But I hold which means to begin utilizing Julia extra, after which there’s Go.

Every so typically, I run throughout a distinct type of downside, and when it’s sufficiently compact, typically I’ll resolve it in just a few languages, simply to study extra about how every addresses the issue.

Recently, a non-programmer colleague launched me to only such an issue. It goes like this:

Many folks residing in neighborhood XYZ battle to make ends meet each day. Employment alternatives locally are restricted and are usually low-paying. The price of residing is relatively excessive: water, electrical energy, and healthcare are costly. Post-secondary schooling, whether or not tutorial or technical, means transferring to the closest metropolis. On the plus aspect, the neighborhood is small and close-knit. People assist one another out as a lot as their circumstances allow.

COVID-19 has hit this neighborhood laborious within the financial sense. Although there have not been any infections but, the 2 principal employers within the city are dealing with monetary damage and have laid off nearly all of their staff. The authorities has helped out, however the quantity of assist will not be sufficient for the households struggling the toughest.

A neighborhood department of a nationwide charity has obtained some funding to offer assist to households in want. Seeking to stretch this funding as a lot as potential, the charity arranges to purchase bulk a number of meals and family provides, then break up the majority tons into household hampers of roughly equal financial worth. Their query is, how to take action?

My colleague thought that maybe I may assist him with a spreadsheet to deal with the distribution. However, to me, this appeared to be the right little downside to unravel with a small program. What would possibly the steps be?

  1. Unpack the majority packages into their particular person items.
  2. While there are nonetheless items left:
    1. Grab a contemporary hamper.
    2. Set the hamper worth to zero.
    3. While the hamper worth is lower than the perfect hamper worth and there are nonetheless items left:
      1. Pick a unit at random.
      2. If that unit is not within the hamper and if the hamper worth would not be too excessive by including it:
        1. Move the unit to the hamper.
        2. Increment the hamper worth by the unit worth.

That looks as if a superb first approximation. It additionally looks as if the right small algorithm to implement in Groovy.

The Groovy resolution

In Java, I discover myself declaring utility courses to carry tuples of information (the brand new report function goes to be nice for that). In Groovy, I have a tendency to make use of the language assist for maps. Let’s use a listing of maps to carry the majority gadgets picked up from the wholesaler:

def packs = [
    [merchandise:'Rice',model:'Best Family',items:10,worth:5650,amount:1],
    [item:'Spaghetti',brand:'Best Family',units:1,price:327,quantity:10],
    [item:'Sardines',brand:'Fresh Caught',units:3,price:2727,quantity:3],
    [item:'Chickpeas',brand:'Southern Style',units:2,price:2600,quantity:5],
    [item:'Lentils',brand:'Southern Style',units:2,price:2378,quantity:5],
    [item:'Vegetable oil',brand:'Crafco',units:12,price:10020,quantity:1],
    [item:'UHT milk',brand:'Atlantic',units:6,price:4560,quantity:2],
    [item:'Flour',brand:'Neighbor Mills',units:10,price:5200,quantity:1],
    [item:'Tomato sauce',brand:'Best Family',units:1,price:190,quantity:10],
    [item:'Sugar',brand:'Good Price',units:1,price:565,quantity:10],
    [item:'Tea',brand:'Superior',units:5,price:2720,quantity:2],
    [item:'Coffee',brand:'Colombia Select',units:2,price:4180,quantity:5],
    [item:'Tofu',brand:'Gourmet Choice',units:1,price:1580,quantity:10],
    [item:'Bleach',brand:'Blanchite',units:5,price:3550,quantity:2],
    [item:'Soap',brand:'Sunny Day',units:6,price:1794,quantity:2]]

There is one bulk pack of 10 luggage of rice and 10 bulk packs with one bag every of spaghetti. In the above, the variable packs is ready to a listing (truly a Java ArrayList beneath) of maps (truly a Java HashMap beneath). Because Groovy is dynamically typed (by default, anyway), I take advantage of def to declare the packs variable and am blissful to have each String and Integer values in my maps.

And sure, these costs do look a bit unusual, however this downside occurred in a spot with a distinct foreign money.

The subsequent step is to unpack these bulk packages. Unpacking the only bulk bundle of rice yields 10 items of rice; that’s, the overall variety of items yielded is items * amount. Groovy offers a helpful operate referred to as accumulateMany that can be utilized to flatten lists of lists, so the code to hold out the unpacking is absolutely simple:

def items = packs.accumulateMany pack ->
    [[merchandise:pack.merchandise, model:pack.model, worth:(pack.worth / pack.items)]] *
                (pack.items * pack.amount)

Note that accumulateMany takes a Closure as its argument; so it is a type of regionally declared operate with a single parameter, pack, that returns a listing of (items * amount) maps, with every map together with the merchandise, model, and calculated unit worth from the corresponding bulk pack. Of be aware right here is that the Groovy multiply operator (*) with a listing on the left aspect and a quantity (N) on the appropriate will produce a listing with the unique gadgets replicated so as N instances.

The last step is to repack the items into the baskets for distribution. But first, I have to get a bit extra particular concerning the splendid hamper worth, and I would as nicely not be overly restrictive when there are only a few items left:

def valueIdeal = 5000
def valueMax = valueIdeal * 1.1

OK! Let’s repack the baskets:

def rnd = new Random()
def hamperNumber = zero    // [1]

whereas (items.measurement()) {  // [2]
    hamperNumber++
    def hamper = []
    def worth = zero       // [2.1]
    for (boolean canAdd = true; canAdd; )
    println ""
    println "Hamper $hamperNumber value $value:"
    hamper.every                                                                   // [2.3]
    println "Remaining units $ average price = $avgPrice" // [2.4]
}

Some clarification, with numbers in brackets within the feedback above (e.g., [1]) comparable to the clarifications under:

  • 1. Initialize Groovy’s random quantity generator and the hamper quantity.
  • 2. This whereas loop will redistribute items into hampers so long as there are extra accessible:
    • 2.1 Increment the hamper quantity, get a brand new empty hamper (a listing of items), and set its worth to zero.
    • 2.2 This for loop will add as many items to the hamper as potential:
      • 2.2.1 Get a random quantity between zero and the variety of remaining items minus 1.
      • 2.2.2 Assume you possibly can’t discover extra items so as to add.
      • 2.2.three This for loop, beginning on the randomly chosen index, will attempt to discover a unit that may be added to the hamper.
        • 2.2.three.1 Figure out which unit to take a look at.
        • 2.2.three.2 Add this unit to the hamper if there are just a few left or if the worth of the hamper is not too excessive as soon as the unit is added.
        • 2.2.three.three Add the unit to the hamper, increment the hamper worth by the unit worth, and take away the unit from the accessible items checklist.
        • 2.2.three.four As lengthy as there are items left, you possibly can add extra, so get away of this loop to maintain wanting.
      • 2.2.four On exit from this for loop, if you happen to inspected each remaining unit and couldn’t discover one so as to add to the hamper, the hamper is full; in any other case, you discovered one and might proceed searching for extra.
    • 2.three Print out the contents of the hamper.
    • 2.four Print out the remaining items information.

When you run this code, the output seems to be like:

Hamper 1 worth 5414:
Vegetable oil            Crafco                    835.00
Coffee                   Colombia Select          2090.00
Tofu                     Gourmet Choice           1580.00
Sardines                 Fresh Caught              909.00
Remaing items 151

Hamper 2 worth 5309:
Flour                    Neighbor Mills            520.00
Sugar                    Good Price                565.00
Vegetable oil            Crafco                    835.00
Coffee                   Colombia Select          2090.00
Rice                     Best Family               565.00
Tomato sauce             Best Family               190.00
Tea                      Superior                  544.00
Remaing items 144

Hamper three worth 5395:
Flour                    Neighbor Mills            520.00
UHT milk                 Atlantic                  760.00
Tomato sauce             Best Family               190.00
Tofu                     Gourmet Choice           1580.00
Spaghetti                Best Family               327.00
Sugar                    Good Price                565.00
Sardines                 Fresh Caught              909.00
Tea                      Superior                  544.00
Remaing items 136

Hamper 23 worth 5148:
Flour                    Neighbor Mills            520.00
Tea                      Superior                  544.00
Chickpeas                Southern Style           1300.00
Lentils                  Southern Style           1189.00
Vegetable oil            Crafco                    835.00
UHT milk                 Atlantic                  760.00
Remaing items three

Hamper 24 worth 3955:
Chickpeas                Southern Style           1300.00
Sugar                    Good Price                565.00
Coffee                   Colombia Select          2090.00
Remaing items zero

The final hamper is abbreviated in contents and worth.

Closing ideas

Note there’s some fiddly enterprise about having the ability to add items to the hamper. Basically, you decide a random place within the checklist of items and, beginning at that place, iterate via the checklist till you both discover a unit whose worth permits it to be included or till you exhaust the checklist. Also, when there are just a few gadgets left, you simply toss them into the final hamper.

Another subject value mentioning: This is not a very environment friendly strategy. Removing components from ArrayLists, letting Groovy use its default BigDecimal, and some different issues make this much less appropriate for an enormous redistribution downside. Still, it runs fairly quickly on my getting old dual-core machine.

And one last thought—utilizing whereas and for ? Really? Not some cool practical code? Afraid so. I could not consider a approach to make use of map and cut back fashion closures in Groovy in collaboration with a random number of items for repackaging. Can you?

In one other article article, I am going to resolve this in Python, and future articles will do it in Java, Julia, and Go.

Most Popular

To Top