diff --git a/currency/pairs.go b/currency/pairs.go index e680c5a6db1..bac29298825 100644 --- a/currency/pairs.go +++ b/currency/pairs.go @@ -481,3 +481,35 @@ func (p Pairs) GetPairsByBase(baseTerm Code) (Pairs, error) { } return pairs, nil } + +// equalKey is a small key for testing pair equality without delimiter +type equalKey struct { + Base *Item + Quote *Item +} + +// FindMatchingPairs returns all pairs that match the incoming pairs. +// Duplications are intentionally removed. +func (p Pairs) FindMatchingPairs(b Pairs) Pairs { + if len(p) == 0 || len(b) == 0 { + return []Pair{} + } + m := map[equalKey]struct{}{} + for i := range p { + m[equalKey{Base: p[i].Base.Item, Quote: p[i].Quote.Item}] = struct{}{} + } + outgoing := func() Pairs { + if len(p) < len(b) { + return make(Pairs, 0, len(p)) + } + return make(Pairs, 0, len(b)) + }() + for i := range b { + key := equalKey{Base: b[i].Base.Item, Quote: b[i].Quote.Item} + if _, ok := m[key]; ok { + outgoing = append(outgoing, b[i]) + delete(m, key) + } + } + return outgoing +} diff --git a/currency/pairs_test.go b/currency/pairs_test.go index 7c4ca9b414a..3a4a94099fe 100644 --- a/currency/pairs_test.go +++ b/currency/pairs_test.go @@ -3,6 +3,7 @@ package currency import ( "encoding/json" "errors" + "fmt" "testing" "github.com/stretchr/testify/assert" @@ -870,3 +871,60 @@ func TestGetPairsByBase(t *testing.T) { t.Fatalf("received: '%v' but expected '%v'", len(got), 3) } } + +func TestFindMatchingPairs(t *testing.T) { + t.Parallel() + + available := Pairs{ + NewPair(BTC, USD), + NewPair(LTC, USD), + NewPair(USD, NZD), + NewPair(LTC, USDT), + NewPair(LTC, DAI), + NewPair(USDT, XRP), + NewPair(DAI, XRP), + NewPair(DAI, XRP), // duplication + } + + testCases := []struct { + incoming Pairs + expected Pairs + }{ + {incoming: Pairs{NewPair(BTC, USD)}, expected: Pairs{NewPair(BTC, USD)}}, + {incoming: Pairs{NewPair(BTC, USD), NewPair(LTC, USD)}, expected: Pairs{NewPair(BTC, USD), NewPair(LTC, USD)}}, + {incoming: Pairs{NewPair(BTC, USD), NewPair(LTC, USD), NewPair(USD, NZD)}, expected: Pairs{NewPair(BTC, USD), NewPair(LTC, USD), NewPair(USD, NZD)}}, + {incoming: Pairs{NewPair(BTC, USD), NewPair(LTC, USD), NewPair(USD, NZD), NewPair(LTC, USDT)}, expected: Pairs{NewPair(BTC, USD), NewPair(LTC, USD), NewPair(USD, NZD), NewPair(LTC, USDT)}}, + {incoming: Pairs{NewPair(BTC, USD), NewPair(LTC, USD), NewPair(USD, NZD), NewPair(LTC, USDT), NewPair(LTC, DAI)}, expected: Pairs{NewPair(BTC, USD), NewPair(LTC, USD), NewPair(USD, NZD), NewPair(LTC, USDT), NewPair(LTC, DAI)}}, + {incoming: Pairs{NewPair(BTC, USD), NewPair(LTC, USD), NewPair(USD, NZD), NewPair(LTC, USDT), NewPair(LTC, DAI), NewPair(USDT, XRP)}, expected: Pairs{NewPair(BTC, USD), NewPair(LTC, USD), NewPair(USD, NZD), NewPair(LTC, USDT), NewPair(LTC, DAI), NewPair(USDT, XRP)}}, + {incoming: Pairs{NewPair(BTC, USD), NewPair(LTC, USD), NewPair(USD, NZD), NewPair(LTC, USDT), NewPair(LTC, DAI), NewPair(USDT, XRP), NewPair(DAI, XRP)}, expected: Pairs{NewPair(BTC, USD), NewPair(LTC, USD), NewPair(USD, NZD), NewPair(LTC, USDT), NewPair(LTC, DAI), NewPair(USDT, XRP), NewPair(DAI, XRP)}}, + /*test for pair not available*/ {incoming: Pairs{NewPair(BTC, USD), NewPair(LTC, USD), NewPair(USD, NZD), NewPair(LTC, USDT), NewPair(LTC, DAI), NewPair(USDT, XRP), NewPair(DAI, XRP), NewPair(WABI, MAD)}, expected: Pairs{NewPair(BTC, USD), NewPair(LTC, USD), NewPair(USD, NZD), NewPair(LTC, USDT), NewPair(LTC, DAI), NewPair(USDT, XRP), NewPair(DAI, XRP)}}, + /*test for duplication*/ {incoming: Pairs{NewPair(BTC, USD), NewPair(BTC, USD), NewPair(LTC, USD), NewPair(USD, NZD), NewPair(LTC, USDT), NewPair(LTC, DAI), NewPair(USDT, XRP), NewPair(DAI, XRP), NewPair(WABI, MAD)}, expected: Pairs{NewPair(BTC, USD), NewPair(LTC, USD), NewPair(USD, NZD), NewPair(LTC, USDT), NewPair(LTC, DAI), NewPair(USDT, XRP), NewPair(DAI, XRP)}}, + } + + for x := range testCases { + tc := testCases[x] + t.Run(fmt.Sprintf("Test %d", x), func(t *testing.T) { + t.Parallel() + require.Equal(t, tc.expected, available.FindMatchingPairs(tc.incoming)) + }) + } +} + +// 2482711 438.9 ns/op 352 B/op 1 allocs/op +func BenchmarkFindMatchingPairs(b *testing.B) { + available := Pairs{ + NewPair(BTC, USD), + NewPair(LTC, USD), + NewPair(USD, NZD), + NewPair(LTC, USDT), + NewPair(LTC, DAI), + NewPair(USDT, XRP), + NewPair(DAI, XRP), + } + + other := Pairs{NewPair(BTC, USD), NewPair(LTC, USD), NewPair(USD, NZD), NewPair(LTC, USDT), NewPair(LTC, DAI), NewPair(USDT, XRP), NewPair(DAI, XRP)} + + for x := 0; x < b.N; x++ { + _ = available.FindMatchingPairs(other) + } +}