Skip to content

Commit ebb0f47

Browse files
committed
Merge branch 'release/v1.4.1'
2 parents c6e1db8 + 792ee3f commit ebb0f47

File tree

6 files changed

+170
-4
lines changed

6 files changed

+170
-4
lines changed

README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Slice printing is **50% faster with 8 items** slice and **250% with 20 items** s
77
![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/wissance/stringFormatter?style=plastic)
88
![GitHub issues](https://img.shields.io/github/issues/wissance/stringFormatter?style=plastic)
99
![GitHub Release Date](https://img.shields.io/github/release-date/wissance/stringFormatter)
10-
![GitHub release (latest by date)](https://img.shields.io/github/downloads/wissance/stringFormatter/v1.3.0/total?style=plastic)
10+
![GitHub release (latest by date)](https://img.shields.io/github/downloads/wissance/stringFormatter/v1.4.1/total?style=plastic)
1111

1212
![String Formatter: a convenient string formatting tool](img/sf_cover.png)
1313

@@ -126,6 +126,13 @@ separator := ","
126126
result := stringFormatter.SliceToString(&slice, &separator)
127127
```
128128

129+
`SliceSameTypeToString` - function that converts typed slice to line with separator
130+
```go
131+
separator := ":"
132+
numericSlice := []int{100, 200, 400, 800}
133+
result := stringFormatter.SliceSameTypeToString(&numericSlice, &separator)
134+
```
135+
129136
#### 2.4 Benchmarks of the SliceToString function
130137

131138
`sf` is rather fast then `fmt` 2.5 times (250%) faster on slice with 20 items, see benchmark:

formatter.go

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -339,9 +339,18 @@ func getItemAsStr(item *any, itemFormat *string) string {
339339
if len(*itemFormat) > 1 {
340340
separator = (*itemFormat)[1:]
341341
}
342+
342343
// slice processing converting to {item}{delimiter}{item}{delimiter}{item}
343-
slice := (*item).([]any)
344-
return SliceToString(&slice, &separator)
344+
slice, ok := (*item).([]any)
345+
if ok {
346+
if len(slice) == 1 {
347+
// this is because slice in 0 item contains another slice, we should take it
348+
slice, ok = slice[0].([]any)
349+
}
350+
return SliceToString(&slice, &separator)
351+
} else {
352+
return convertSliceToStrWithTypeDiscover(item, &separator)
353+
}
345354
default:
346355
base = 10
347356
}
@@ -419,3 +428,56 @@ func getItemAsStr(item *any, itemFormat *string) string {
419428

420429
return argStr
421430
}
431+
432+
func convertSliceToStrWithTypeDiscover(slice *any, separator *string) string {
433+
// 1. attempt to convert to int
434+
iSlice, ok := (*slice).([]int)
435+
if ok {
436+
return SliceSameTypeToString(&iSlice, separator)
437+
}
438+
439+
// 2. attempt to convert to string
440+
sSlice, ok := (*slice).([]string)
441+
if ok {
442+
return SliceSameTypeToString(&sSlice, separator)
443+
}
444+
445+
// 3. attempt to convert to float64
446+
f64Slice, ok := (*slice).([]float64)
447+
if ok {
448+
return SliceSameTypeToString(&f64Slice, separator)
449+
}
450+
451+
// 4. attempt to convert to float32
452+
f32Slice, ok := (*slice).([]float32)
453+
if ok {
454+
return SliceSameTypeToString(&f32Slice, separator)
455+
}
456+
457+
// 5. attempt to convert to bool
458+
bSlice, ok := (*slice).([]bool)
459+
if ok {
460+
return SliceSameTypeToString(&bSlice, separator)
461+
}
462+
463+
// 6. attempt to convert to int64
464+
i64Slice, ok := (*slice).([]int64)
465+
if ok {
466+
return SliceSameTypeToString(&i64Slice, separator)
467+
}
468+
469+
// 7. attempt to convert to uint
470+
uiSlice, ok := (*slice).([]uint)
471+
if ok {
472+
return SliceSameTypeToString(&uiSlice, separator)
473+
}
474+
475+
// 8. attempt to convert to int32
476+
i32Slice, ok := (*slice).([]int32)
477+
if ok {
478+
return SliceSameTypeToString(&i32Slice, separator)
479+
}
480+
481+
// default way ...
482+
return fmt.Sprintf("%v", *slice)
483+
}

formatter_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,61 @@ func TestFormatWithArgFormatting(t *testing.T) {
207207
}
208208
}
209209

210+
func TestFormatWithArgFormattingForTypedSlice(t *testing.T) {
211+
for name, test := range map[string]struct {
212+
template string
213+
args []any
214+
expected string
215+
}{
216+
"list_with_int_slice": {
217+
template: "This is a list(slice) test: {0:L-}",
218+
args: []any{[]int{101, 202, 303}},
219+
expected: "This is a list(slice) test: 101-202-303",
220+
},
221+
"list_with_uint_slice": {
222+
template: "This is a list(slice) test: {0:L-}",
223+
args: []any{[]uint{102, 204, 308}},
224+
expected: "This is a list(slice) test: 102-204-308",
225+
},
226+
"list_with_int32_slice": {
227+
template: "This is a list(slice) test: {0:L-}",
228+
args: []any{[]int32{100, 200, 300}},
229+
expected: "This is a list(slice) test: 100-200-300",
230+
},
231+
"list_with_int64_slice": {
232+
template: "This is a list(slice) test: {0:L-}",
233+
args: []any{[]int64{1001, 2002, 3003}},
234+
expected: "This is a list(slice) test: 1001-2002-3003",
235+
},
236+
"list_with_float64_slice": {
237+
template: "This is a list(slice) test: {0:L-}",
238+
args: []any{[]float64{1.01, 2.02, 3.03}},
239+
expected: "This is a list(slice) test: 1.01-2.02-3.03",
240+
},
241+
"list_with_float32_slice": {
242+
template: "This is a list(slice) test: {0:L-}",
243+
args: []any{[]float32{5.01, 6.02, 7.03}},
244+
expected: "This is a list(slice) test: 5.01-6.02-7.03",
245+
},
246+
"list_with_bool_slice": {
247+
template: "This is a list(slice) test: {0:L-}",
248+
args: []any{[]bool{true, true, false}},
249+
expected: "This is a list(slice) test: true-true-false",
250+
},
251+
"list_with_string_slice": {
252+
template: "This is a list(slice) test: {0:L-}",
253+
args: []any{[]string{"s1", "s2", "s3"}},
254+
expected: "This is a list(slice) test: s1-s2-s3",
255+
},
256+
} {
257+
// Run test here
258+
t.Run(name, func(t *testing.T) {
259+
// assert.NotNil(t, test)
260+
assert.Equal(t, test.expected, stringFormatter.Format(test.template, test.args...))
261+
})
262+
}
263+
}
264+
210265
// TestStrFormatWithComplicatedText - this test represents issue with complicated text
211266
func TestFormatComplex(t *testing.T) {
212267
for name, test := range map[string]struct {

slicetostring.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,23 @@ func SliceToString(data *[]any, separator *string) string {
2323

2424
return sliceStr.String()
2525
}
26+
27+
func SliceSameTypeToString[T any](data *[]T, separator *string) string {
28+
if len(*data) == 0 {
29+
return ""
30+
}
31+
32+
sliceStr := &strings.Builder{}
33+
// init memory initially
34+
sliceStr.Grow(len(*data)*len(*separator)*2 + (len(*data)-1)*len(*separator))
35+
isFirst := true
36+
for _, item := range *data {
37+
if !isFirst {
38+
sliceStr.WriteString(*separator)
39+
}
40+
sliceStr.WriteString(Format("{0}", item))
41+
isFirst = false
42+
}
43+
44+
return sliceStr.String()
45+
}

slicetostring_benchmark_test.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@ func BenchmarkSliceToStringAdvanced10MixedItems(b *testing.B) {
2929
}
3030
}
3131

32+
func BenchmarkSliceToStringAdvanced10TypedItems(b *testing.B) {
33+
slice := []int32{100, 102, 300, 404, 500, 600, 707, 800, 909, 1024}
34+
for i := 0; i < b.N; i++ {
35+
_ = stringFormatter.Format("{0:L,}", []any{slice})
36+
}
37+
}
38+
3239
func BenchmarkSliceStandard10MixedItems(b *testing.B) {
3340
slice := []any{100, "200", 300, "400", 500, 600, "700", 800, 1.09, "hello"}
3441
for i := 0; i < b.N; i++ {
@@ -39,9 +46,17 @@ func BenchmarkSliceStandard10MixedItems(b *testing.B) {
3946
func BenchmarkSliceToStringAdvanced20StrItems(b *testing.B) {
4047
slice := []any{"str1", "str2", "str3", "str4", "str5", "str6", "str7", "str8", "str9", "str10",
4148
"str11", "str12", "str13", "str14", "str15", "str16", "str17", "str18", "str19", "str20"}
49+
for i := 0; i < b.N; i++ {
50+
_ = stringFormatter.Format("{0:L,}", []any{slice})
51+
}
52+
}
53+
54+
func BenchmarkSliceToStringAdvanced20TypedStrItems(b *testing.B) {
55+
slice := []string{"str1", "str2", "str3", "str4", "str5", "str6", "str7", "str8", "str9", "str10",
56+
"str11", "str12", "str13", "str14", "str15", "str16", "str17", "str18", "str19", "str20"}
4257
separator := ","
4358
for i := 0; i < b.N; i++ {
44-
_ = stringFormatter.SliceToString(&slice, &separator)
59+
_ = stringFormatter.SliceSameTypeToString(&slice, &separator)
4560
}
4661
}
4762

slicetostring_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,10 @@ func TestSliceToString(t *testing.T) {
2929
})
3030
}
3131
}
32+
33+
func TestSliceSameTypeToString(t *testing.T) {
34+
separator := ":"
35+
numericSlice := []int{100, 200, 400, 800}
36+
result := stringFormatter.SliceSameTypeToString(&numericSlice, &separator)
37+
assert.Equal(t, "100:200:400:800", result)
38+
}

0 commit comments

Comments
 (0)