Skip to content

Commit 904496b

Browse files
authored
Add AllRepeat function (#16)
1 parent 9657a1a commit 904496b

File tree

2 files changed

+243
-0
lines changed

2 files changed

+243
-0
lines changed

combinations.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,34 @@ func All[T any](set []T) (subsets [][]T) {
2727
return subsets
2828
}
2929

30+
// AllRepeat returns all combinations with repetitions for a given slice,
31+
// from 1 up to a maximum combination length of m.
32+
func AllRepeat[T any](set []T, m int) (subsets [][]T) {
33+
if m < 1 {
34+
return nil
35+
}
36+
37+
var generateCombos func([]T, int)
38+
generateCombos = func(current []T, depth int) {
39+
if depth == 0 {
40+
subset := make([]T, len(current))
41+
copy(subset, current)
42+
subsets = append(subsets, subset)
43+
return
44+
}
45+
46+
for _, item := range set {
47+
generateCombos(append(current, item), depth-1)
48+
}
49+
}
50+
51+
for length := 1; length <= m; length++ {
52+
generateCombos([]T{}, length)
53+
}
54+
55+
return subsets
56+
}
57+
3058
// Combinations returns combinations of n elements for a given generic array.
3159
// For n < 1, it equals to All and returns all combinations.
3260
func Combinations[T any](set []T, n int) (subsets [][]T) {

combinations_test.go

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,3 +264,218 @@ func TestStringCombinationsN(t *testing.T) {
264264
})
265265
}
266266
}
267+
268+
func TestAllWithRepetitions(t *testing.T) {
269+
tt := []struct {
270+
name string
271+
in []string
272+
m int
273+
out [][]string
274+
}{
275+
{
276+
name: "Empty slice",
277+
in: []string{},
278+
m: 1,
279+
out: nil,
280+
},
281+
{
282+
name: "Single item, m = 1",
283+
in: []string{"A"},
284+
m: 1,
285+
out: [][]string{
286+
{"A"},
287+
},
288+
},
289+
{
290+
name: "Single item, m = 2",
291+
in: []string{"A"},
292+
m: 2,
293+
out: [][]string{
294+
{"A"},
295+
{"A", "A"},
296+
},
297+
},
298+
{
299+
name: "Two items, m = 1",
300+
in: []string{"A", "B"},
301+
m: 1,
302+
out: [][]string{
303+
{"A"},
304+
{"B"},
305+
},
306+
},
307+
{
308+
name: "Two items, m = 2",
309+
in: []string{"A", "B"},
310+
m: 2,
311+
out: [][]string{
312+
{"A"},
313+
{"B"},
314+
{"A", "A"},
315+
{"A", "B"},
316+
{"B", "A"},
317+
{"B", "B"},
318+
},
319+
},
320+
{
321+
name: "Three items, m = 2",
322+
in: []string{"A", "B", "C"},
323+
m: 2,
324+
out: [][]string{
325+
{"A"},
326+
{"B"},
327+
{"C"},
328+
{"A", "A"},
329+
{"A", "B"},
330+
{"A", "C"},
331+
{"B", "A"},
332+
{"B", "B"},
333+
{"B", "C"},
334+
{"C", "A"},
335+
{"C", "B"},
336+
{"C", "C"},
337+
},
338+
},
339+
{
340+
name: "Two items, m = 3",
341+
in: []string{"A", "B"},
342+
m: 3,
343+
out: [][]string{
344+
{"A"},
345+
{"B"},
346+
{"A", "A"},
347+
{"A", "B"},
348+
{"B", "A"},
349+
{"B", "B"},
350+
{"A", "A", "A"},
351+
{"A", "A", "B"},
352+
{"A", "B", "A"},
353+
{"A", "B", "B"},
354+
{"B", "A", "A"},
355+
{"B", "A", "B"},
356+
{"B", "B", "A"},
357+
{"B", "B", "B"},
358+
},
359+
},
360+
}
361+
362+
for _, tc := range tt {
363+
t.Run(tc.name, func(t *testing.T) {
364+
out := AllRepeat(tc.in, tc.m)
365+
if !reflect.DeepEqual(out, tc.out) {
366+
t.Errorf("error: \nreturn:\t%v\nwant:\t%v", out, tc.out)
367+
}
368+
})
369+
}
370+
}
371+
372+
func TestAllWithRepetitionsInt(t *testing.T) {
373+
tt := []struct {
374+
name string
375+
in []int
376+
m int
377+
out [][]int
378+
}{
379+
{
380+
name: "Two items, m = 0",
381+
in: []int{1, 2},
382+
m: 0,
383+
out: nil,
384+
},
385+
{
386+
name: "Empty slice",
387+
in: []int{},
388+
m: 1,
389+
out: nil,
390+
},
391+
{
392+
name: "Single item, m = 1",
393+
in: []int{1},
394+
m: 1,
395+
out: [][]int{
396+
{1},
397+
},
398+
},
399+
{
400+
name: "Single item, m = 2",
401+
in: []int{1},
402+
m: 2,
403+
out: [][]int{
404+
{1},
405+
{1, 1},
406+
},
407+
},
408+
{
409+
name: "Two items, m = 2",
410+
in: []int{1, 2},
411+
m: 2,
412+
out: [][]int{
413+
{1},
414+
{2},
415+
{1, 1},
416+
{1, 2},
417+
{2, 1},
418+
{2, 2},
419+
},
420+
},
421+
{
422+
name: "Three items, m = 1",
423+
in: []int{1, 2, 3},
424+
m: 1,
425+
out: [][]int{
426+
{1},
427+
{2},
428+
{3},
429+
},
430+
},
431+
{
432+
name: "Three items, m = 2",
433+
in: []int{1, 2, 3},
434+
m: 2,
435+
out: [][]int{
436+
{1},
437+
{2},
438+
{3},
439+
{1, 1},
440+
{1, 2},
441+
{1, 3},
442+
{2, 1},
443+
{2, 2},
444+
{2, 3},
445+
{3, 1},
446+
{3, 2},
447+
{3, 3},
448+
},
449+
},
450+
{
451+
name: "Two items, m = 3",
452+
in: []int{1, 2},
453+
m: 3,
454+
out: [][]int{
455+
{1},
456+
{2},
457+
{1, 1},
458+
{1, 2},
459+
{2, 1},
460+
{2, 2},
461+
{1, 1, 1},
462+
{1, 1, 2},
463+
{1, 2, 1},
464+
{1, 2, 2},
465+
{2, 1, 1},
466+
{2, 1, 2},
467+
{2, 2, 1},
468+
{2, 2, 2},
469+
},
470+
},
471+
}
472+
473+
for _, tc := range tt {
474+
t.Run(tc.name, func(t *testing.T) {
475+
out := AllRepeat(tc.in, tc.m)
476+
if !reflect.DeepEqual(out, tc.out) {
477+
t.Errorf("error: \nreturn:\t%v\nwant:\t%v", out, tc.out)
478+
}
479+
})
480+
}
481+
}

0 commit comments

Comments
 (0)