I got it, the reason is that my slice contains a sub slice
so the copy
worked for the top one but did not work for the sub one, which is sharing same data, I fixed it by re-writing the code by creating a new slice, pushing the data to it, then use it to replace the original one, as below:
func (i *Inventories) CloneFrom(c Inventories) {
inv := new(Inventories)
for _, v := range c {
batches := Lots{}
for _, b := range v.Batches {
batches = append(batches, Lot{
Date: b.Date,
Key: b.Key,
Value: b.Value,
})
}
*inv = append(*inv, Inventory{
Warehouse: v.Warehouse,
Item: v.Item,
Batches: batches,
})
}
(*i).ReplaceBy(inv)
}
func (i *Inventories) ReplaceBy(x *Inventories) {
*i = *x
}
My full code is:
package main
import (
"fmt"
"log"
"math"
"sort"
"time"
)
const (
layoutISO = "2006-01-02"
layoutUS = "January 2, 2006"
custom = "1/2/2006"
)
type Inventories []Inventory
type Inventory struct { //instead of: map[string]map[string]Pairs
Warehouse string
Item string
Batches Lots
}
type Lots []Lot
type Lot struct {
Date time.Time
Key string
Value float64
}
func main() {
fmt.Println("Hello, 世界")
date := "12/31/2019" // "1999-12-31"
t, _ := time.Parse(custom, date)
var inventories = new(Inventories)
var inventory = Inventory{} // new(Inventory) // Or = Inventory{} both are working //warehouse[item[batch, qty]]
inventory.Warehouse = "DMM"
inventory.Item = "Helmet"
inventory.Batches = append(inventory.Batches, Lot{t, "Jan", 20})
inventory.InsertBatch(Lot{time.Now(), "", -30})
inventory.Batches.Insert(Lot{time.Now(), "Feb", 60})
x := Inventory{
Warehouse: "DMM",
Item: "Gloves",
Batches: Lots{
Lot{mustTime(time.Parse(custom, "1/7/2020")), "Jan", 50},
Lot{mustTime(time.Parse(custom, "2/1/2020")), "Feb", 70},
Lot{mustTime(time.Parse(custom, "1/5/2020")), "", -10},
Lot{mustTime(time.Parse(custom, "2/9/2020")), "", -30},
},
}
// Below can be used for grouping batches in each warehouse seperatly, this can be combined in the lines under
// inventory.Batches.Group()
// x.GroupBatches()
// Above can be replaced by below
inventories.Insert(inventory)
inventories.Insert(x)
outs := inventories.Clone()
outs.Reductions()
ins := inventories.Clone()
ins.Additions()
fmt.Printf("\n\n[1] Ins: \n%v", ins)
fmt.Printf("\n\n[2] Outs: \n%v", outs)
//ins_clone := ins.Clone()
batchesOut, batchesBalance := outs.BuildBatchesFrom(ins)
fmt.Printf("\n\n[1] Ins: \n%v", ins)
fmt.Printf("\n\n[2] Outs: \n%v", outs)
fmt.Printf("\n\n[4] Batches outs: \n%v", batchesOut)
fmt.Printf("\n\n[5] Batches Balances: \n%v", batchesBalance)
}
func (i *Inventories) Insert(x Inventory) {
*i = append(*i, x)
}
func (i *Inventories) GroupBatches() {
inv := new(Inventories)
for _, el := range *i {
el.GroupBatches()
inv.Insert(el)
}
(*i).ReplaceBy(inv)
}
func (i *Inventories) SortBatches() {
inv := new(Inventories)
for _, el := range *i {
sort.Sort(Lots(el.Batches))
inv.Insert(el)
}
(*i).ReplaceBy(inv)
}
func (i *Inventories) Additions() {
inv := new(Inventories)
for _, el := range *i {
for _, b := range el.Batches {
if b.Value > 0 {
if index := inv.Find(el.Warehouse, el.Item); index == len(*inv) {
*inv = append(*inv, Inventory{
Warehouse: el.Warehouse,
Item: el.Item,
Batches: Lots{b},
})
} else {
inv.InsertBatchAt(index, b)
}
}
}
}
(*i).ReplaceBy(inv)
}
func (i *Inventories) Reductions() {
inv := new(Inventories)
for _, el := range *i {
for _, b := range el.Batches {
if b.Value < 0 {
if index := inv.Find(el.Warehouse, el.Item); index == len(*inv) {
*inv = append(*inv, Inventory{
Warehouse: el.Warehouse,
Item: el.Item,
Batches: Lots{b},
})
} else {
inv.InsertBatchAt(index, b)
}
}
}
}
(*i).ReplaceBy(inv)
}
// Do NOT USE IT, replace it by CloneFrom()
func (c *Inventories) Clone() Inventories {
var s = make(Inventories, len(*c))
copy(s, *c)
return s
}
func (i *Inventories) CloneFrom(c Inventories) {
inv := new(Inventories)
for _, v := range c {
batches := Lots{}
for _, b := range v.Batches {
batches = append(batches, Lot{
Date: b.Date,
Key: b.Key,
Value: b.Value,
})
}
*inv = append(*inv, Inventory{
Warehouse: v.Warehouse,
Item: v.Item,
Batches: batches,
})
}
(*i).ReplaceBy(inv)
}
// Find returns the smallest index i at which x == a[i],
// or len(a) if there is no such index.
func (inv *Inventories) Find(whs string, itm string) int {
for i, n := range *inv {
if whs == n.Warehouse && itm == n.Item {
return i
}
}
return len(*inv)
}
func (outs Inventories) BuildBatchesFrom(ins Inventories) (batchesBalance Inventories, outgoing Inventories) {
batchesBalance.CloneFrom(ins) // := ins.Clone()
outgoing.CloneFrom(outs) // := outs.Clone()
batchesOut := Inventories{}
for _, in := range batchesBalance {
for _, out := range outgoing {
if out.Warehouse == in.Warehouse && out.Item == in.Item {
batches := Lots{}
OUTER:
for {
oldestBatch := in.Batches.First()
batchQty := math.Min(in.Batches.First().Value, math.Abs(out.Batches.First().Value))
batches = append(batches, Lot{out.Batches.First().Date, oldestBatch.Key, batchQty})
out.Batches[0].Value = out.Batches.First().Value + batchQty
in.Batches[0].Value = oldestBatch.Value - batchQty
if in.Batches.First().Value == 0 {
in.Batches.PopFirst()
}
if out.Batches.First().Value == 0 {
out.Batches.PopFirst()
if len(out.Batches) == 0 {
break
} else {
continue OUTER
}
} else {
continue OUTER
}
}
batchesOut = append(batchesOut, Inventory{
Warehouse: out.Warehouse,
Item: out.Item,
Batches: batches,
})
}
}
//os.Exit(3)
}
return batchesOut, batchesBalance
}
func (i *Inventories) ReplaceBy(x *Inventories) {
*i = *x
}
func (i *Inventories) InsertBatchAt(index int, b Lot) {
(*i)[index].Batches.Insert(b)
//a = append(a[:index+1], a[index:]...)
//*i = append(*i, x)
}
func (i *Inventory) InsertBatch(x Lot) {
(*i).Batches = append((*i).Batches, x)
}
func (i *Inventory) GroupBatches() {
(*i).Batches.Group()
}
func (p *Lots) Group() {
lots := new(Lots)
lots.FromMap(p.Map())
p.ReplaceBy(lots)
}
func (p *Lots) Pop(i int) {
*p = append((*p)[:i], (*p)[i+1:]...) // append(a, b...) // append(a[:i], a[i+1:]...)
}
func (p *Lots) PopLast() {
*p = append((*p)[:len(*p)-1])
}
func (p *Lots) First() Lot {
return (*p)[0]
}
func (p *Lots) Last() Lot {
return (*p)[len(*p)]
}
func (p *Lots) PopFirst() {
*p = append((*p)[1:len(*p)])
}
func (p *Lots) FromMap(m map[string]float64) {
for k, v := range m {
(*p).Insert(Lot{time.Now(), k, v})
}
}
// Below to enable sorting: sort.Sort(Lots(lots))
func (l Lots) Len() int { return len(l) }
func (l Lots) Less(i, j int) bool { return (l[i].Date).Before(l[j].Date) } // { return l[i].Key < l[j].Key }
func (l Lots) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
func (p *Lots) Insert(x Lot) {
*p = append(*p, x)
}
func (p *Lots) ReplaceBy(x *Lots) {
*p = *x
}
func (p *Lots) Map() map[string]float64 {
sum := make(map[string]float64)
for _, el := range *p {
sum[el.Key] = sum[el.Key] + el.Value
}
return sum
}
type Interface interface {
// Len is the number of elements in the collection.
Len() int
// Less reports whether the element with
// index i should sort before the element with index j.
Less(i, j int) bool
// Swap swaps the elements with indexes i and j.
Swap(i, j int)
}
func mustTime(t time.Time, err error) time.Time {
failOnError(err)
return t
}
func failOnError(err error) {
if err != nil {
log.Fatal("Error:", err)
panic(err)
}
}
// Contains tells whether a contains x.
func Contains(a []string, x string) bool {
for _, n := range a {
if x == n {
return true
}
}
return false
}