I have two channels, first give me some strings, which I need filter to same values, then result need sent to second channel
func main() {
c := make(chan string, 5)
o := make(chan string, 5)
arr := []string{"aa", "ab", "ab", "bb", "bb", "ba", "cc"}
for _, v := range arr {
c <- v
go removeDuplicates(c, o)
time.Sleep(1 * time.Second)
fmt.Println("output: ", <-o)
}
}
func removeDuplicates(cIn, cOut chan string) {
last := ""
for cur, isOpen := <-cIn; isOpen; {
if cur != last {
fmt.Printf("val: %s, last: %s\n", cur, last)
last = cur
cOut <- cur
//close(cOut)
}
}
}
I try save previous value to "last" variable, but when I run the program, "last" is empty
val: aa, last:
output: aa
val: ab, last:
output: ab
val: ab, last:
also I don't know when and which channels need to be closed in this situation. Thank you for your help and attention
removeDuplicates()
The problem is that you have an empty post statement in your for
statement:
for cur, isOpen := <-cIn; isOpen; {
// ..
}
So you receive once from the cIn
channel, but you never receive more, you do nothing in the post statement, so you just repeat the loop body, endlessly.
Once the loop body is executed, you have to receive again:
for cur, isOpen := <-cIn; isOpen; cur, isOpen = <-cIn {
// ..
}
With this, output will be (try it on the Go Playground):
val: aa, last:
output: aa
val: ab, last: aa
output: ab
val: ab, last:
output: ab
val: bb, last: ab
output: bb
val: bb, last:
output: bb
val: ba, last: ab
output: ba
val: cc, last:
output: cc
But best would be to use for range
over the channel:
for cur := range cIn {
if cur != last {
fmt.Printf("val: %s, last: %s\n", cur, last)
last = cur
cOut <- cur
}
}
This outputs the same. Try this one on the Go Playground.
main()
We see "invalid" output, values in output are still duplicated.
This is because you launch multiple goroutines running removeDuplicates()
. This is bad because the values sent on the input channel will be received by multiple goroutines, and if the duplicated values are not received by one, they can still be detected as unique, hence the same value will be sent more than once to the output.
Have a single producer sending all values on the input channel, and once all values were sent, close the channel.
Have a single goroutine filtering the values, using for range
, and once the loop exits (all input values are consumed), close the output channel.
And have a single goroutine receive values from the output channel, using for range
, so you can eliminate that ugly time.Sleep
:
func main() {
c := make(chan string, 5)
o := make(chan string, 5)
go func() {
arr := []string{"aa", "ab", "ab", "bb", "bb", "ba", "cc"}
for _, v := range arr {
c <- v
}
close(c)
}()
go removeDuplicates(c, o)
for v := range o {
fmt.Println("output: ", v)
}
}
func removeDuplicates(cIn chan string, cOut chan string) {
last := ""
for cur := range cIn {
if cur != last {
fmt.Printf("val: %s, last: %s\n", cur, last)
last = cur
cOut <- cur
}
}
close(cOut)
}
This will output (try it on the Go Playground):
val: aa, last:
val: ab, last: aa
val: bb, last: ab
val: ba, last: bb
val: cc, last: ba
output: aa
output: ab
output: bb
output: ba
output: cc