4
\$\begingroup\$

Would the following be considered idiomatic Go code?

My main concerns are:

  1. The use of maps for this purpose. I basically want the data structure to be completely dynamic in size and have easy access to elements by id. This is because I will want to retrieve specific items on demand lots of times, so I don't want to loop each time.

  2. The check for the map element

  3. My use of pointers

  4. General flow and structure

package main

    import (
        "fmt"
    )

    type BoxItem struct {
        Id  int
        Qty int
    }


    func NewBox() *Box {
        return &Box{make(map[int]*BoxItem), 0}
    }

    type Box struct {
        BoxItems map[int]*BoxItem
    }

    func (this *Box) AddBoxItem(id int, qty int) *Box {

        i, ok := this.BoxItems[id]

        if ok {
            i.Qty += qty
        } else {
            boxItem := &BoxItem{id, qty}
            this.BoxItems[id] = boxItem
        }

        return this
    }

    func main() {

        box := NewBox()
        box.AddBoxItem(1, 1)

        fmt.Printf("There are %d items in the box", len(box.BoxItems))
    }
\$\endgroup\$

3 Answers 3

4
\$\begingroup\$

Instead of

i, ok := this.BoxItems[id]
if ok {
    i.Qty += qty
} 

a more idiomatic usage is

if i, ok := this.BoxItems[id]; ok {
    i.Qty += qty
} 

Calling the receiver this doesn't seem like a good practice to me. Calling it box would be clearer.

You seem to want to make your method chainable. I'm not sure this is a frequent practice in Go but it's probably more a matter of style and experience than a question of idiomatic code or not.

The rest could be condensed a little but that's not a matter of idiom. I have nothing to say regarding your use of pointers.

\$\endgroup\$
2
  • \$\begingroup\$ I didnt set out to make it chainable, can you point to where you think i have purposely done this? Was it because the add method returns the box instance? and also where you think it can be condensed? \$\endgroup\$ Aug 8, 2013 at 18:18
  • \$\begingroup\$ Seems chainable because the AddBoxItem function returns the Box reference. \$\endgroup\$ Sep 3, 2014 at 16:57
3
\$\begingroup\$

In Go, you should hide the data structures and other implementation details of a box, then you can start with some plausible implementation of a box and later improve it if necessary. Implement box as a Go package. For example,

box.go:

package box

func New() *Box {
    return &Box{items: make(map[int]int)}
}

type Box struct {
    items map[int]int
}

func (b *Box) NumItems() int {
    return len(b.items)
}

func (b *Box) AddItem(id int, qty int) {
    b.items[id] += qty
}

func (b *Box) ItemQuantity(id int) int {
    return b.items[id]
}

main.go:

package main

import (
    "box"
    "fmt"
)

func main() {
    b := box.New()
    id, qty := 1, 42
    b.AddItem(id, 1)
    b.AddItem(id, qty-1)
    fmt.Printf("There are %d items in the b.\n", b.NumItems())
    fmt.Printf("There are %d of item %d in the b.\n", b.ItemQuantity(id), id)
    id = 7
    fmt.Printf("There are %d of item %d in the b.\n", b.ItemQuantity(id), id)
}

Output:

There are 1 items in the box.
There are 42 of item 1 in the box.
There are 0 of item 7 in the box.
\$\endgroup\$
1
  • \$\begingroup\$ BTW, both here and in the original, using *Box (other than as a receiver in methods that need to initialize the map itself, i.e. make) is undesirable for a type that only contains a simple map. It just adds an extra level of indirection. E.g. just use func New() Box { return Box{…} }; func (b Box) NumItems(), etc. \$\endgroup\$
    – Dave C
    May 3, 2015 at 17:57
2
\$\begingroup\$

So far, everything you want is provided by a map, so with just the requirements you've given in the original code, I'd use a map to keep things as simple as possible. You can later add methods to Box if you need them, but will have to refactor if you need to store more information than just quantities.

// An ID uniquely identifies a type of item that goes in a Box.
type ID int

// A Box holds quantities of items (indexed by their ID).
type Box map[ID]int

If you want to know how many items you have:

n := box[someID]

If you want to add items to the box:

box[someID] += 3
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.