From 5ac6622fccc126d94a53c3a690ac0fe46cc7a90c Mon Sep 17 00:00:00 2001 From: Viktor Nikolaiev Date: Thu, 30 May 2024 01:42:35 +0300 Subject: [PATCH] Add examples for slices (#17) --- README.md | 64 ++++++++++++++++++++++++++++++++++++++++++++++++- example_test.go | 44 +++++++++++++++++++++++++++++++++- 2 files changed, 106 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 94e2df4..eb8c78c 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,28 @@ go get github.com/destel/rill ## Example usage -Consider an application that fetches keys from multiple URLs, retrieves their values from a key-value database in batches, and prints them. +A basic example demonstrating how **ForEach** can be used to process a list of items concurrently and handle errors. + +[Full runnable example](https://pkg.go.dev/github.com/destel/rill#example-package-Basic) + +```go +func main() { + items := rill.FromSlice([]string{"item1", "item2", "item3", "item4", "item5", "item6", "item7", "item8", "item9", "item10"}, nil) + + err := rill.ForEach(items, 3, func(item string) error { + randomSleep(1000 * time.Millisecond) // simulate some additional work + res := strings.ToUpper(item) + fmt.Println(res) + return nil + }) + if err != nil { + fmt.Println("Error:", err) + } +} +``` + + +Consider a more advanced example: an application that fetches keys from multiple URLs, retrieves their values from a key-value database in batches, and prints them. This example demonstrates the library's strengths in handling concurrent tasks, error propagation, batching and data streaming, all while maintaining simplicity and efficiency. @@ -327,5 +348,46 @@ func main() { func getTemperature(city string, date time.Time) (float64, error) { // ... } +``` + + +## Working with slices +Rill is designed for channel based workflows, but it can also be used with slices, thanks to its ability +to do ordered processing. Example below demonstrates how to create a **mapSLice** generic helper function that +does parallel slice processing. That helper is then used to fetch users from an API concurrently. + +[Full runnable example](https://pkg.go.dev/github.com/destel/rill#example-package-Slices) + +```go +type User struct { + ID int + Username string +} + +// mapSLice is a helper function that does a parallel map operation on a slice of items +func mapSLice[A, B any](in []A, n int, f func(A) (B, error)) ([]B, error) { + inChan := rill.FromSlice(in, nil) + outChan := rill.OrderedMap(inChan, n, f) + return rill.ToSlice(outChan) +} + +func main() { + ids := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + + users, err := mapSLice(ids, 3, getUser) + if err != nil { + fmt.Println("Error:", err) + return + } + + fmt.Printf("%+v\n", users) +} + + + +// getUser fetches a user from an API +func getUser(id int) (User, error) { + // ... +} ``` \ No newline at end of file diff --git a/example_test.go b/example_test.go index 521fd3d..5d04cc6 100644 --- a/example_test.go +++ b/example_test.go @@ -21,7 +21,12 @@ type Measurement struct { Temp float64 } -// A basic demonstrating how [ForEach] can be used to process a list of items concurrently. +type User struct { + ID int + Username string +} + +// A basic example demonstrating how [ForEach] can be used to process a list of items concurrently. func Example_basic() { items := rill.FromSlice([]string{"item1", "item2", "item3", "item4", "item5", "item6", "item7", "item8", "item9", "item10"}, nil) @@ -36,6 +41,31 @@ func Example_basic() { } } +// Rill is designed for channel based workflows, but it can also be used with slices, thanks to its ability +// to do ordered processing. Example below demonstrates how you can create a **mapSLice** generic helper function that +// does parallel slice processing. That helper is then used to fetch users from an API concurrently. +func Example_slices() { + startedAt := time.Now() + defer func() { fmt.Println("Elapsed:", time.Since(startedAt)) }() + + ids := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + + users, err := mapSLice(ids, 3, getUser) + if err != nil { + fmt.Println("Error:", err) + return + } + + fmt.Printf("%+v\n", users) +} + +// mapSLice is a helper function that does a parallel map operation on a slice of items +func mapSLice[A, B any](in []A, n int, f func(A) (B, error)) ([]B, error) { + inChan := rill.FromSlice(in, nil) + outChan := rill.OrderedMap(inChan, n, f) + return rill.ToSlice(outChan) +} + // This example fetches keys from a list of URLs, retrieves their values from a key-value database, and prints them. // The pipeline leverages concurrency for fetching and processing and uses batching to reduce the number of database calls. func Example_batching() { @@ -211,3 +241,15 @@ func getTemperature(city string, date time.Time) (float64, error) { func randomSleep(max time.Duration) { time.Sleep(time.Duration(rand.Intn(int(max)))) } + +// getUser simulates fetching a user from an API, introducing a randomized delay to simulate network latency. +func getUser(id int) (User, error) { + randomSleep(1000 * time.Millisecond) // Simulate a network delay + + // generate random name adj+noun + adj := []string{"big", "small", "fast", "slow", "smart", "happy", "sad", "funny", "serious"} + noun := []string{"dog", "cat", "bird", "fish", "mouse", "elephant", "lion", "tiger", "bear", "wolf"} + username := fmt.Sprintf("%s_%s", adj[rand.Intn(len(adj))], noun[rand.Intn(len(noun))]) + + return User{ID: id, Username: username}, nil +}