How to sort/filter a slice of custom structure after time.Time field

1,701 views
Skip to first unread message

Mike

unread,
Feb 20, 2012, 10:41:57 AM2/20/12
to golan...@googlegroups.com
Let's assume a slice of following collection:

type Invoice struct {
   Id     int
   Due   time.Time
}

How do You sort after Due and filter e.g. all Invoices due <= today.
Any advice? 

Jan Mercl

unread,
Feb 20, 2012, 4:19:04 PM2/20/12
to golan...@googlegroups.com
Sort easy by implementing sort.Interface. Filter using range perhaps?

Patrick Mylund Nielsen

unread,
Feb 20, 2012, 4:22:28 PM2/20/12
to Mike, golan...@googlegroups.com
You can use sort.Sort if you make a new type for your slice and attach
the method needed for comparison:

package main

import (
"fmt"
"sort"
"time"
)

type Invoice struct {
Id int64
Due time.Time
}

type Invoices []Invoice

func (s Invoices) Len() int {
return len(s)
}

func (s Invoices) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}

func (s Invoices) Less(i, j int) bool {
return s[i].Due.Before(s[j].Due)
}

func main() {
day := 1*24*time.Hour
is := Invoices{
{1, time.Now()},
{2, time.Now().Add(1*day)},
{3, time.Now().Add(2*day)},
{4, time.Now().Add(5*day)},
{5, time.Now().Add(7*day)},
{6, time.Now().Add(-1*day)},
{7, time.Now().Add(-2*day)},
}
sort.Sort(is)
limit := time.Now().Add(1*day)
for i, v := range is {
if v.Due.After(limit) {
is = is[:i]
break
}
}
fmt.Println(is)
}

# go run slice.go
[{7 2012-02-18 22:21:57.285126 +0100 CET} {6 2012-02-19
22:21:57.285126 +0100 CET} {1 2012-02-20 22:21:57.285126 +0100 CET} {2
2012-02-21 22:21:57.285126 +0100 CET}]

Mike

unread,
Feb 21, 2012, 2:54:25 AM2/21/12
to golan...@googlegroups.com, Mike
Tx Patrick, made my day as a Go learner. May I add a related question hopefully interesting for others as well:
Let's assume:

type Activity struct {
        Id int64
        Start time.Time
        End  time.Time


}

type Activities []Activity

1) Your code helps to e.g. sort after Start and filter all activities starting before end of one chosen day.
2) How could I sort afterwards (means the interim result of 1) after End and filter all activities ending after beginning of chosen day.


Patrick Mylund Nielsen

unread,
Feb 21, 2012, 5:31:06 PM2/21/12
to Mike, golan...@googlegroups.com
You're welcome!

I'm not sure I completely understand your question, but if you wanted
to you could make a similar construction where Invoices.Less returns
i.End.Less(j.End), and do "is = is[i:]" instead of "is = is[:i]" in
the for loop to get the later ones (since the slice is sorted by
date.)

If you want to get more advanced you could change your type to a
Struct, add a "SortBy" field, set that before sorting, and then vary
the output from Less/Swap/Len according to s.SortBy. (Or make a struct
with func fields "less", "swap", "len", change the Less/Swap/Len to
just return s.less(i, j)/s.swap(i, j), s.len(), and then set those
fields to what you want.)

If you want to be able to sort in very different ways, you can make
several types with varying Less/Swap/Len methods, then cast your slice
to the appropriate one when passing it to sort, e.g.:

type InvoicesByEnd []Invoices

func (s InvoicesByEnd) Len() {
...
}
func (s InvoicesByEnd) Less(i, j) {
...
}

func (s InvoicesByEnd) Swap(i, j) {
...
}

s := []Invoices{
foo,
bar,
baz,
}

sort.Sort(s.(InvoicesByEnd))

But at some point, looking into "database/sql" might be a better idea? :)

Kyle Lemons

unread,
Feb 21, 2012, 6:02:14 PM2/21/12
to Patrick Mylund Nielsen, Mike, golan...@googlegroups.com
While the before(end) might work, it's not guaranteed to do so.  With this method, it's possible that i < j and j < i, which breaks one of the invariant assumptions of Sort.  You can simply sort it like normal and use your same slicing method.

Patrick Mylund Nielsen

unread,
Feb 21, 2012, 6:10:22 PM2/21/12
to Kyle Lemons, Mike, golan...@googlegroups.com
I don't follow. Why would i < j and j < i? Isn't time.Time.Before
essentially just int64 < oint64?

How would you sort a slice of structs by a certain field if you don't
indicate that e.g. by satisfying the interface with custom methods?
(The example sorts []Struct, not []time.Time)

Apropos, sort.SortBy(slice, "fieldname") (using reflect) might be an
interesting addition to the sort package?

Kyle Lemons

unread,
Feb 21, 2012, 6:25:20 PM2/21/12
to Patrick Mylund Nielsen, Mike, golan...@googlegroups.com
Oh, I misinterpreted the code (and got a bit confused by this proportional font's i and j).  I thought you were saying something like 

func (s Slice) Less(i, j int) { return s[i] < s.end }

or something.  Apologies for the noise.

Patrick Mylund Nielsen

unread,
Feb 21, 2012, 6:28:47 PM2/21/12
to Kyle Lemons, Mike, golan...@googlegroups.com
Apology is on me :) Looking back, my second implementation was very
confusing -- s/i/s[i]/; s/j/s[j]/; s/Less/Before/
Reply all
Reply to author
Forward
0 new messages