Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@ jobs:
name: Update coverage badge
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
persist-credentials: false # otherwise, the token used is the GITHUB_TOKEN, instead of your personal access token.
fetch-depth: 0 # otherwise, there would be errors pushing refs to the destination repository.

- name: Setup go
uses: actions/setup-go@v3
uses: actions/setup-go@v5
with:
go-version: '1.18'
go-version: '1.24.x'

- uses: actions/cache@v3
- uses: actions/cache@v4
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
Expand Down Expand Up @@ -58,4 +58,4 @@ jobs:
uses: ad-m/github-push-action@master
with:
github_token: ${{ github.token }}
branch: ${{ github.head_ref }}
branch: ${{ github.head_ref }}
6 changes: 3 additions & 3 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v3
uses: actions/setup-go@v5
with:
go-version: 1.18
go-version: '1.24.x'

- name: Test
run: go test -v ./...
223 changes: 222 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# gofn
![Coverage](https://img.shields.io/badge/Coverage-87.7%25-brightgreen)
![Coverage](https://img.shields.io/badge/Coverage-88.7%25-brightgreen)
[![Go](https://github.com/devalexandre/gofn/actions/workflows/go.yml/badge.svg)](https://github.com/devalexandre/gofn/actions/workflows/go.yml)

# library to use golang functional
Expand Down Expand Up @@ -31,6 +31,16 @@
- [array.Shift](#arrayshift)
- [array.Sort](#arraysort)
- [array.GroupBy](#arraygroupby)
- [array.GroupSumBy](#arraygroupsumby)
- [array.GroupSumByWhere](#arraygroupsumbywhere)
- [array.GroupCountBy](#arraygroupcountby)
- [array.GroupReduceBy](#arraygroupreduceby)
- [array.GroupStatsBy](#arraygroupstatsby)
- [array.DistinctBy](#arraydistinctby)
- [array.IndexBy](#arrayindexby)
- [array.Partition](#arraypartition)
- [array.SortBy](#arraysortby)
- [array.Take, array.Skip, array.Chunk](#arraytake-arrayskip-arraychunk)
- [chaining functions](#chaining-functions)


Expand Down Expand Up @@ -379,6 +389,160 @@ type Itens struct {
fmt.Println(len(grouped)) // 4

```

### array.GroupSumBy
Group items by a key and sum a numeric value for each group.

```go
type Itens struct {
Name string
Price float64
Description string
Qty int
}

itens := []Itens{
{"Item 1", 10.0, "Description 1", 1},
{"Item 2", 20.0, "Description 2", 2},
{"Item 3", 30.0, "Description 3", 3},
{"Item 4", 40.0, "Description 4", 10},
{"Item 4", 40.0, "Description 4", 15},
{"Item 4", 40.0, "Description 4", 25},
}

priceByName := array.GroupSumBy(itens,
func(item Itens) string { return item.Name },
func(item Itens) float64 { return item.Price },
)

qtyByName := array.GroupSumBy(itens,
func(item Itens) string { return item.Name },
func(item Itens) int { return item.Qty },
)

fmt.Println(priceByName["Item 4"]) // 120
fmt.Println(qtyByName["Item 4"]) // 50
```

### array.GroupSumByWhere
Filter, group and sum in one pass.

```go
priceByName := array.GroupSumByWhere(itens,
func(item Itens) bool { return item.Qty > 3 },
func(item Itens) string { return item.Name },
func(item Itens) float64 { return item.Price },
)

fmt.Println(priceByName["Item 4"]) // 120
```

### array.GroupCountBy
Count rows by a key.

```go
countByName := array.GroupCountBy(itens, func(item Itens) string {
return item.Name
})

fmt.Println(countByName["Item 4"]) // 3
```

### array.GroupReduceBy
Build custom summaries by group.

```go
type Summary struct {
TotalPrice float64
TotalQty int
}

summaryByName := array.GroupReduceBy(itens,
func(item Itens) string {
return item.Name
},
func(acc Summary, item Itens) Summary {
acc.TotalPrice += item.Price
acc.TotalQty += item.Qty
return acc
},
)

fmt.Println(summaryByName["Item 4"]) // {120 50}
```

### array.GroupStatsBy
Calculate count, sum, min, max and average by group.

```go
statsByName := array.GroupStatsBy(itens,
func(item Itens) string { return item.Name },
func(item Itens) float64 { return item.Price },
)

fmt.Println(statsByName["Item 4"].Count) // 3
fmt.Println(statsByName["Item 4"].Sum) // 120
fmt.Println(statsByName["Item 4"].Avg) // 40
```

### array.DistinctBy
Keep the first row for each key.

```go
uniqueByName := array.DistinctBy(itens, func(item Itens) string {
return item.Name
})

fmt.Println(len(uniqueByName)) // 4
```

### array.IndexBy
Create a map by key. If a key repeats, the last row wins.

```go
itemByName := array.IndexBy(itens, func(item Itens) string {
return item.Name
})

fmt.Println(itemByName["Item 4"].Qty) // 25
```

### array.Partition
Split rows into matched and unmatched slices.

```go
highQty, lowQty := array.Partition(itens, func(item Itens) bool {
return item.Qty > 3
})

fmt.Println(len(highQty)) // 3
fmt.Println(len(lowQty)) // 3
```

### array.SortBy
Sort rows by a selected field without mutating the original slice.

```go
byQty := array.SortBy(itens, func(item Itens) int {
return item.Qty
})

fmt.Println(byQty[0].Qty) // 1
```

### array.Take, array.Skip, array.Chunk
Use these helpers for pagination and batch processing.

```go
firstPage := array.Take(itens, 2)
nextRows := array.Skip(itens, 2)
batches := array.Chunk(itens, 2)

fmt.Println(len(firstPage)) // 2
fmt.Println(len(nextRows)) // 4
fmt.Println(len(batches)) // 3
```

## chaining functions

You can chain the functions together.
Expand All @@ -398,3 +562,60 @@ data := array.Array[int]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
fmt.Println(a) // [4 8 12 16 20]

```

You can also group and sum values after filtering.

```go
type Itens struct {
Name string
Price float64
Description string
Qty int
}

data := array.Array[Itens]{
{"Item 1", 10.0, "Description 1", 1},
{"Item 2", 20.0, "Description 2", 2},
{"Item 3", 30.0, "Description 3", 3},
{"Item 4", 40.0, "Description 4", 10},
{"Item 4", 40.0, "Description 4", 15},
{"Item 4", 40.0, "Description 4", 25},
}

priceByName := data.
Filter(func(item Itens) bool {
return item.Qty > 3
}).
GroupSumBy(
func(item Itens) string { return item.Name },
func(item Itens) float64 { return item.Price },
)

fmt.Println(priceByName["Item 4"]) // 120
```

Some helpers are also available in chain form.

```go
countByName := data.GroupCountBy(func(item Itens) string {
return item.Name
})

statsByName := data.GroupStatsBy(
func(item Itens) string { return item.Name },
func(item Itens) float64 { return item.Price },
)

uniqueByName := data.DistinctBy(func(item Itens) string {
return item.Name
})

highQty, lowQty := data.Partition(func(item Itens) bool {
return item.Qty > 3
})

page := data.
SortByFloat64(func(item Itens) float64 { return item.Price }).
Skip(10).
Take(10)
```
55 changes: 55 additions & 0 deletions array/array.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,58 @@ func (a Array[T]) Sort(f func(i, j int) bool) Array[T] {
func (a Array[T]) GroupBy(f func(T) string) map[string][]T {
return GroupBy(a, f)
}

func (a Array[T]) GroupSumBy(key func(T) string, value func(T) float64) map[string]float64 {
return GroupSumBy(a, key, value)
}

func (a Array[T]) GroupSumByWhere(where func(T) bool, key func(T) string, value func(T) float64) map[string]float64 {
return GroupSumByWhere(a, where, key, value)
}

func (a Array[T]) GroupCountBy(key func(T) string) map[string]int {
return GroupCountBy(a, key)
}

func (a Array[T]) GroupStatsBy(key func(T) string, value func(T) float64) map[string]GroupStats[float64] {
return GroupStatsBy(a, key, value)
}

func (a Array[T]) DistinctBy(key func(T) string) Array[T] {
return DistinctBy(a, key)
}

func (a Array[T]) IndexBy(key func(T) string) map[string]T {
return IndexBy(a, key)
}

func (a Array[T]) Partition(f func(T) bool) (Array[T], Array[T]) {
matched, unmatched := Partition(a, f)
return matched, unmatched
}

func (a Array[T]) SortByString(key func(T) string) Array[T] {
return SortBy(a, key)
}

func (a Array[T]) SortByFloat64(key func(T) float64) Array[T] {
return SortBy(a, key)
}

func (a Array[T]) Take(n int) Array[T] {
return Take(a, n)
}

func (a Array[T]) Skip(n int) Array[T] {
return Skip(a, n)
}

func (a Array[T]) Chunk(size int) []Array[T] {
chunks := Chunk(a, size)
result := make([]Array[T], len(chunks))
for i, chunk := range chunks {
result[i] = chunk
}

return result
}
Loading
Loading