Skip to content

Commit 842435d

Browse files
committed
Added FindFirst,FindLast,FindAll
1 parent 89bc33d commit 842435d

File tree

4 files changed

+171
-0
lines changed

4 files changed

+171
-0
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ All notable changes to **ValueStringBuilder** will be documented in this file. T
66

77
## [Unreleased]
88

9+
### Added
10+
- Added the following methods: `FindFirst`, `FindLast`, `FindAll`
11+
912
## [0.3.0] - 2022-12-25
1013

1114
### Added

search.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package Text
2+
3+
// Returns all occurences of needle in haystack
4+
func findAll(haystack []rune, needle string) []int {
5+
6+
if len(haystack) == 0 || len(needle) == 0 || len(needle) > len(haystack) {
7+
return []int{}
8+
}
9+
10+
items := make([]int, 0, 8)
11+
12+
for i := 0; i < len(haystack); i++ {
13+
for j := 0; j < len(needle); j++ {
14+
if haystack[i+j] != rune(needle[j]) {
15+
break
16+
}
17+
18+
if j == len(needle)-1 {
19+
items = append(items, i)
20+
}
21+
}
22+
}
23+
24+
return items
25+
}
26+
27+
// Returns the first occurrence of haystack in needle or -1 if not found.
28+
func findFirst(haystack []rune, needle string) int {
29+
if len(haystack) == 0 || len(needle) == 0 || len(needle) > len(haystack) {
30+
return -1
31+
}
32+
33+
lenHaystack := len(haystack)
34+
lenNeedle := len(needle)
35+
36+
for i := 0; i <= lenHaystack-lenNeedle; i++ {
37+
for j := 0; j < lenNeedle; j++ {
38+
if haystack[i+j] != rune(needle[j]) {
39+
break
40+
}
41+
42+
if j == lenNeedle-1 {
43+
return i
44+
}
45+
}
46+
}
47+
48+
return -1
49+
}
50+
51+
// Returns the last occurrence of haystack in needle or -1 if not found.
52+
func findLast(haystack []rune, needle string) int {
53+
if len(haystack) == 0 || len(needle) == 0 || len(needle) > len(haystack) {
54+
return -1
55+
}
56+
57+
lenHaystack := len(haystack)
58+
lenNeedle := len(needle)
59+
60+
for i := lenHaystack - lenNeedle + 1; i >= 0; i-- {
61+
for j := 0; j < lenNeedle; j++ {
62+
if haystack[i+j] != rune(needle[j]) {
63+
break
64+
}
65+
66+
if j == lenNeedle-1 {
67+
return i
68+
}
69+
}
70+
}
71+
72+
return -1
73+
}

stringbuilder.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,21 @@ func (s *StringBuilder) AsRune() []rune {
121121
return s.data[:s.position]
122122
}
123123

124+
// Returns the first occurrence of the given text in the string builder. Returns -1 if not found
125+
func (s *StringBuilder) FindFirst(text string) int {
126+
return findFirst(s.AsRune(), text)
127+
}
128+
129+
// Returns the last occurrence of the given text in the string builder. Returns -1 if not found
130+
func (s *StringBuilder) FindLast(text string) int {
131+
return findLast(s.AsRune(), text)
132+
}
133+
134+
// Returns all occurrences of the given text in the string builder. Returns an empty if no occurrence found.
135+
func (s *StringBuilder) FindAll(text string) []int {
136+
return findAll(s.AsRune(), text)
137+
}
138+
124139
func (s *StringBuilder) grow(lenToAdd int) {
125140
// Grow times 2 until lenToAdd fits
126141
newLen := cap(s.data)

stringbuilder_test.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,3 +192,83 @@ func TestAppendRuneMultiple(t *testing.T) {
192192
t.Errorf("Actual %q, Expected: %q", result, expected)
193193
}
194194
}
195+
196+
func TestFindFirst(t *testing.T) {
197+
tests := []struct {
198+
name string
199+
input string
200+
needle string
201+
want int
202+
}{
203+
{"Empty haystack", "", "n", -1},
204+
{"Empty needle", "n", "", -1},
205+
{"Needle longer than haystack", "a", "ab", -1},
206+
{"Hello in Hello World", "Hello World", "Hello", 0},
207+
}
208+
for _, tt := range tests {
209+
t.Run(tt.name, func(t *testing.T) {
210+
s := NewStringBuilderFromString(tt.input)
211+
if got := s.FindFirst(tt.needle); got != tt.want {
212+
t.Errorf("StringBuilder.FindFirst() = %v, want %v", got, tt.want)
213+
}
214+
})
215+
}
216+
}
217+
218+
func TestFindLast(t *testing.T) {
219+
tests := []struct {
220+
name string
221+
input string
222+
needle string
223+
want int
224+
}{
225+
{"Empty haystack", "", "n", -1},
226+
{"Empty needle", "n", "", -1},
227+
{"Needle longer than haystack", "a", "ab", -1},
228+
{"Hello in Hello World", "Hello World", "Hello", 0},
229+
}
230+
for _, tt := range tests {
231+
t.Run(tt.name, func(t *testing.T) {
232+
s := NewStringBuilderFromString(tt.input)
233+
if got := s.FindLast(tt.needle); got != tt.want {
234+
t.Errorf("StringBuilder.FindLast() = %v, want %v", got, tt.want)
235+
}
236+
})
237+
}
238+
}
239+
240+
func TestFindAll(t *testing.T) {
241+
tests := []struct {
242+
name string
243+
input string
244+
needle string
245+
want []int
246+
}{
247+
{"Empty haystack", "", "n", []int{}},
248+
{"Empty needle", "n", "", []int{}},
249+
{"Needle longer than haystack", "a", "ab", []int{}},
250+
{"Hello in Hello World", "Hello World", "Hello", []int{0}},
251+
}
252+
for _, tt := range tests {
253+
t.Run(tt.name, func(t *testing.T) {
254+
s := NewStringBuilderFromString(tt.input)
255+
if got := s.FindAll(tt.needle); !slicesEqual(got, tt.want) {
256+
t.Errorf("StringBuilder.FindLast() = %v, want %v", got, tt.want)
257+
}
258+
})
259+
}
260+
}
261+
262+
func slicesEqual(a []int, b []int) bool {
263+
if len(a) != len(b) {
264+
return false
265+
}
266+
267+
for i := 0; i < len(a); i++ {
268+
if a[i] != b[i] {
269+
return false
270+
}
271+
}
272+
273+
return true
274+
}

0 commit comments

Comments
 (0)