...

Source file src/github.com/thoas/go-funk/transform_test.go

Documentation: github.com/thoas/go-funk

     1  package funk
     2  
     3  import (
     4  	"database/sql"
     5  	"fmt"
     6  	"reflect"
     7  	"testing"
     8  
     9  	"github.com/stretchr/testify/assert"
    10  	"github.com/stretchr/testify/require"
    11  )
    12  
    13  func TestMap(t *testing.T) {
    14  	is := assert.New(t)
    15  
    16  	r := Map([]int{1, 2, 3, 4}, func(x int) string {
    17  		return "Hello"
    18  	})
    19  
    20  	result, ok := r.([]string)
    21  
    22  	is.True(ok)
    23  	is.Equal(len(result), 4)
    24  
    25  	r = Map([]int{1, 2, 3, 4}, func(x int) (int, int) {
    26  		return x, x
    27  	})
    28  
    29  	resultType := reflect.TypeOf(r)
    30  
    31  	is.True(resultType.Kind() == reflect.Map)
    32  	is.True(resultType.Key().Kind() == reflect.Int)
    33  	is.True(resultType.Elem().Kind() == reflect.Int)
    34  
    35  	mapping := map[int]string{
    36  		1: "Florent",
    37  		2: "Gilles",
    38  	}
    39  
    40  	r = Map(mapping, func(k int, v string) int {
    41  		return k
    42  	})
    43  
    44  	is.True(reflect.TypeOf(r).Kind() == reflect.Slice)
    45  	is.True(reflect.TypeOf(r).Elem().Kind() == reflect.Int)
    46  
    47  	r = Map(mapping, func(k int, v string) (string, string) {
    48  		return fmt.Sprintf("%d", k), v
    49  	})
    50  
    51  	resultType = reflect.TypeOf(r)
    52  
    53  	is.True(resultType.Kind() == reflect.Map)
    54  	is.True(resultType.Key().Kind() == reflect.String)
    55  	is.True(resultType.Elem().Kind() == reflect.String)
    56  }
    57  
    58  func TestFlatMap(t *testing.T) {
    59  
    60  	is := assert.New(t)
    61  
    62  	x := reflect.Value{}.IsValid()
    63  	fmt.Println(x)
    64  
    65  	r := FlatMap([][]int{{1}, {2}, {3}, {4}}, func(x []int) []int {
    66  		return x
    67  	})
    68  
    69  	result, ok := r.([]int)
    70  
    71  	is.True(ok)
    72  	is.ElementsMatch(result, []int{1, 2, 3, 4})
    73  
    74  	mapping := map[string][]int{
    75  		"a": {1},
    76  		"b": {2},
    77  	}
    78  
    79  	r = FlatMap(mapping, func(k string, v []int) []int {
    80  		return v
    81  	})
    82  
    83  	result, ok = r.([]int)
    84  
    85  	is.True(ok)
    86  	is.ElementsMatch(result, []int{1, 2})
    87  }
    88  
    89  func TestToMap(t *testing.T) {
    90  	is := assert.New(t)
    91  
    92  	f := &Foo{
    93  		ID:        1,
    94  		FirstName: "Dark",
    95  		LastName:  "Vador",
    96  		Age:       30,
    97  		Bar: &Bar{
    98  			Name: "Test",
    99  		},
   100  	}
   101  
   102  	results := []*Foo{f}
   103  
   104  	instanceMap := ToMap(results, "ID")
   105  
   106  	is.True(reflect.TypeOf(instanceMap).Kind() == reflect.Map)
   107  
   108  	mapping, ok := instanceMap.(map[int]*Foo)
   109  
   110  	is.True(ok)
   111  
   112  	for _, result := range results {
   113  		item, ok := mapping[result.ID]
   114  
   115  		is.True(ok)
   116  		is.True(reflect.TypeOf(item).Kind() == reflect.Ptr)
   117  		is.True(reflect.TypeOf(item).Elem().Kind() == reflect.Struct)
   118  
   119  		is.Equal(item.ID, result.ID)
   120  	}
   121  }
   122  
   123  func TestChunk(t *testing.T) {
   124  	is := assert.New(t)
   125  
   126  	results := Chunk([]int{0, 1, 2, 3, 4}, 2).([][]int)
   127  
   128  	is.Len(results, 3)
   129  	is.Len(results[0], 2)
   130  	is.Len(results[1], 2)
   131  	is.Len(results[2], 1)
   132  
   133  	is.Len(Chunk([]int{}, 2), 0)
   134  	is.Len(Chunk([]int{1}, 2), 1)
   135  	is.Len(Chunk([]int{1, 2, 3}, 0), 3)
   136  }
   137  
   138  func TestFlatten(t *testing.T) {
   139  	is := assert.New(t)
   140  
   141  	is.Equal(Flatten([][][]int{{{1, 2}}, {{3, 4}}}), [][]int{{1, 2}, {3, 4}})
   142  }
   143  
   144  func TestFlattenDeep(t *testing.T) {
   145  	is := assert.New(t)
   146  
   147  	is.Equal(FlattenDeep([][][]int{{{1, 2}}, {{3, 4}}}), []int{1, 2, 3, 4})
   148  }
   149  
   150  func TestShuffle(t *testing.T) {
   151  	initial := []int{0, 1, 2, 3, 4}
   152  
   153  	results := Shuffle(initial)
   154  
   155  	is := assert.New(t)
   156  
   157  	is.Len(results, 5)
   158  
   159  	for _, entry := range initial {
   160  		is.True(Contains(results, entry))
   161  	}
   162  }
   163  
   164  func TestReverse(t *testing.T) {
   165  	results := Reverse([]int{0, 1, 2, 3, 4})
   166  
   167  	is := assert.New(t)
   168  
   169  	is.Equal(Reverse("abcdefg"), "gfedcba")
   170  	is.Len(results, 5)
   171  
   172  	is.Equal(results, []int{4, 3, 2, 1, 0})
   173  }
   174  
   175  func TestUniq(t *testing.T) {
   176  	is := assert.New(t)
   177  
   178  	results := Uniq([]int{0, 1, 1, 2, 3, 0, 0, 12})
   179  	is.Len(results, 5)
   180  	is.Equal(results, []int{0, 1, 2, 3, 12})
   181  
   182  	results = Uniq([]string{"foo", "bar", "foo", "bar", "bar"})
   183  	is.Len(results, 2)
   184  	is.Equal(results, []string{"foo", "bar"})
   185  }
   186  
   187  func TestConvertSlice(t *testing.T) {
   188  	instances := []*Foo{foo, foo2}
   189  
   190  	var raw []Model
   191  
   192  	ConvertSlice(instances, &raw)
   193  
   194  	is := assert.New(t)
   195  
   196  	is.Len(raw, len(instances))
   197  }
   198  
   199  func TestDrop(t *testing.T) {
   200  	results := Drop([]int{0, 1, 1, 2, 3, 0, 0, 12}, 3)
   201  
   202  	is := assert.New(t)
   203  
   204  	is.Len(results, 5)
   205  
   206  	is.Equal([]int{2, 3, 0, 0, 12}, results)
   207  }
   208  
   209  func TestPrune(t *testing.T) {
   210  
   211  	var testCases = []struct {
   212  		OriginalFoo *Foo
   213  		Paths       []string
   214  		ExpectedFoo *Foo
   215  	}{
   216  		{
   217  			foo,
   218  			[]string{"FirstName"},
   219  			&Foo{
   220  				FirstName: foo.FirstName,
   221  			},
   222  		},
   223  		{
   224  			foo,
   225  			[]string{"FirstName", "ID"},
   226  			&Foo{
   227  				FirstName: foo.FirstName,
   228  				ID:        foo.ID,
   229  			},
   230  		},
   231  		{
   232  			foo,
   233  			[]string{"EmptyValue.Int64"},
   234  			&Foo{
   235  				EmptyValue: sql.NullInt64{
   236  					Int64: foo.EmptyValue.Int64,
   237  				},
   238  			},
   239  		},
   240  		{
   241  			foo,
   242  			[]string{"FirstName", "ID", "EmptyValue.Int64"},
   243  			&Foo{
   244  				FirstName: foo.FirstName,
   245  				ID:        foo.ID,
   246  				EmptyValue: sql.NullInt64{
   247  					Int64: foo.EmptyValue.Int64,
   248  				},
   249  			},
   250  		},
   251  		{
   252  			foo,
   253  			[]string{"FirstName", "ID", "EmptyValue.Int64"},
   254  			&Foo{
   255  				FirstName: foo.FirstName,
   256  				ID:        foo.ID,
   257  				EmptyValue: sql.NullInt64{
   258  					Int64: foo.EmptyValue.Int64,
   259  				},
   260  			},
   261  		},
   262  		{
   263  			foo,
   264  			[]string{"FirstName", "ID", "Bar"},
   265  			&Foo{
   266  				FirstName: foo.FirstName,
   267  				ID:        foo.ID,
   268  				Bar:       foo.Bar,
   269  			},
   270  		},
   271  		{
   272  			foo,
   273  			[]string{"Bar", "Bars"},
   274  			&Foo{
   275  				Bar:  foo.Bar,
   276  				Bars: foo.Bars,
   277  			},
   278  		},
   279  		{
   280  			foo,
   281  			[]string{"FirstName", "Bars.Name"},
   282  			&Foo{
   283  				FirstName: foo.FirstName,
   284  				Bars: []*Bar{
   285  					{Name: bar.Name},
   286  					{Name: bar.Name},
   287  				},
   288  			},
   289  		},
   290  		{
   291  			foo,
   292  			[]string{"Bars.Name", "Bars.Bars.Name"},
   293  			&Foo{
   294  				Bars: []*Bar{
   295  					{Name: bar.Name, Bars: []*Bar{{Name: "Level1-1"}, {Name: "Level1-2"}}},
   296  					{Name: bar.Name, Bars: []*Bar{{Name: "Level1-1"}, {Name: "Level1-2"}}},
   297  				},
   298  			},
   299  		},
   300  		{
   301  			foo,
   302  			[]string{"BarInterface", "BarPointer"},
   303  			&Foo{
   304  				BarInterface: bar,
   305  				BarPointer:   &bar,
   306  			},
   307  		},
   308  	}
   309  
   310  	// pass to prune by pointer to struct
   311  	for idx, tc := range testCases {
   312  		t.Run(fmt.Sprintf("Prune pointer test case #%v", idx), func(t *testing.T) {
   313  			is := assert.New(t)
   314  			res, err := Prune(tc.OriginalFoo, tc.Paths)
   315  			require.NoError(t, err)
   316  
   317  			fooPrune := res.(*Foo)
   318  			is.Equal(tc.ExpectedFoo, fooPrune)
   319  		})
   320  	}
   321  
   322  	// pass to prune by struct directly
   323  	for idx, tc := range testCases {
   324  		t.Run(fmt.Sprintf("Prune non pointer test case #%v", idx), func(t *testing.T) {
   325  			is := assert.New(t)
   326  			fooNonPtr := *tc.OriginalFoo
   327  			res, err := Prune(fooNonPtr, tc.Paths)
   328  			require.NoError(t, err)
   329  
   330  			fooPrune := res.(Foo)
   331  			is.Equal(*tc.ExpectedFoo, fooPrune)
   332  		})
   333  	}
   334  
   335  	// test PruneByTag
   336  	var TagTestCases = []struct {
   337  		OriginalFoo *Foo
   338  		Paths       []string
   339  		ExpectedFoo *Foo
   340  		Tag         string
   341  	}{
   342  		{
   343  			foo,
   344  			[]string{"tag 1", "tag 4.BarName"},
   345  			&Foo{
   346  				FirstName: foo.FirstName,
   347  				Bar: &Bar{
   348  					Name: bar.Name,
   349  				},
   350  			},
   351  			"tag_name",
   352  		},
   353  	}
   354  
   355  	for idx, tc := range TagTestCases {
   356  		t.Run(fmt.Sprintf("PruneByTag test case #%v", idx), func(t *testing.T) {
   357  			is := assert.New(t)
   358  			fooNonPtr := *tc.OriginalFoo
   359  			res, err := PruneByTag(fooNonPtr, tc.Paths, tc.Tag)
   360  			require.NoError(t, err)
   361  
   362  			fooPrune := res.(Foo)
   363  			is.Equal(*tc.ExpectedFoo, fooPrune)
   364  		})
   365  	}
   366  
   367  	t.Run("Bar Slice", func(t *testing.T) {
   368  		barSlice := []*Bar{bar, bar}
   369  		barSlicePruned, err := pruneByTag(barSlice, []string{"Name"}, nil /*tag*/)
   370  		require.NoError(t, err)
   371  		assert.Equal(t, []*Bar{{Name: bar.Name}, {Name: bar.Name}}, barSlicePruned)
   372  	})
   373  
   374  	t.Run("Bar Array", func(t *testing.T) {
   375  		barArr := [2]*Bar{bar, bar}
   376  		barArrPruned, err := pruneByTag(barArr, []string{"Name"}, nil /*tag*/)
   377  		require.NoError(t, err)
   378  		assert.Equal(t, [2]*Bar{{Name: bar.Name}, {Name: bar.Name}}, barArrPruned)
   379  	})
   380  
   381  	// test values are copied and not referenced in return result
   382  	// NOTE: pointers at the end of path are referenced. Maybe we need to make a copy
   383  	t.Run("Copy Value Str", func(t *testing.T) {
   384  		is := assert.New(t)
   385  		fooTest := &Foo{
   386  			Bar: &Bar{
   387  				Name: "bar",
   388  			},
   389  		}
   390  		res, err := pruneByTag(fooTest, []string{"Bar.Name"}, nil)
   391  		require.NoError(t, err)
   392  		fooTestPruned := res.(*Foo)
   393  		is.Equal(fooTest, fooTestPruned)
   394  
   395  		// change pruned
   396  		fooTestPruned.Bar.Name = "changed bar"
   397  		// check original is unchanged
   398  		is.Equal(fooTest.Bar.Name, "bar")
   399  	})
   400  
   401  	// error cases
   402  	var errCases = []struct {
   403  		InputFoo *Foo
   404  		Paths    []string
   405  		TagName  *string
   406  	}{
   407  		{
   408  			foo,
   409  			[]string{"NotExist"},
   410  			nil,
   411  		},
   412  		{
   413  			foo,
   414  			[]string{"FirstName.NotExist", "LastName"},
   415  			nil,
   416  		},
   417  		{
   418  			foo,
   419  			[]string{"LastName", "FirstName.NotExist"},
   420  			nil,
   421  		},
   422  		{
   423  			foo,
   424  			[]string{"LastName", "Bars.NotExist"},
   425  			nil,
   426  		},
   427  		// tags
   428  		{
   429  			foo,
   430  			[]string{"tag 999"},
   431  			&[]string{"tag_name"}[0],
   432  		},
   433  		{
   434  			foo,
   435  			[]string{"tag 1.NotExist"},
   436  			&[]string{"tag_name"}[0],
   437  		},
   438  		{
   439  			foo,
   440  			[]string{"tag 4.NotExist"},
   441  			&[]string{"tag_name"}[0],
   442  		},
   443  		{
   444  			foo,
   445  			[]string{"FirstName"},
   446  			&[]string{"tag_name_not_exist"}[0],
   447  		},
   448  	}
   449  
   450  	for idx, errTC := range errCases {
   451  		t.Run(fmt.Sprintf("error test case #%v", idx), func(t *testing.T) {
   452  			_, err := pruneByTag(errTC.InputFoo, errTC.Paths, errTC.TagName)
   453  			assert.Error(t, err)
   454  		})
   455  	}
   456  }
   457  
   458  func ExamplePrune() {
   459  	type ExampleFoo struct {
   460  		ExampleFooPtr *ExampleFoo `json:"example_foo_ptr"`
   461  		Name          string      `json:"name"`
   462  		Number        int         `json:"number"`
   463  	}
   464  
   465  	exampleFoo := ExampleFoo{
   466  		ExampleFooPtr: &ExampleFoo{
   467  			Name:   "ExampleFooPtr",
   468  			Number: 2,
   469  		},
   470  		Name:   "ExampleFoo",
   471  		Number: 1,
   472  	}
   473  
   474  	// prune using struct field name
   475  	res, _ := Prune(exampleFoo, []string{"ExampleFooPtr.Name", "Number"})
   476  	prunedFoo := res.(ExampleFoo)
   477  	fmt.Println(prunedFoo.ExampleFooPtr.Name)
   478  	fmt.Println(prunedFoo.ExampleFooPtr.Number)
   479  	fmt.Println(prunedFoo.Name)
   480  	fmt.Println(prunedFoo.Number)
   481  
   482  	// prune using struct json tag
   483  	res2, _ := PruneByTag(exampleFoo, []string{"example_foo_ptr.name", "number"}, "json")
   484  	prunedByTagFoo := res2.(ExampleFoo)
   485  	fmt.Println(prunedByTagFoo.ExampleFooPtr.Name)
   486  	fmt.Println(prunedByTagFoo.ExampleFooPtr.Number)
   487  	fmt.Println(prunedByTagFoo.Name)
   488  	fmt.Println(prunedByTagFoo.Number)
   489  	// output:
   490  	// ExampleFooPtr
   491  	// 0
   492  	//
   493  	// 1
   494  	// ExampleFooPtr
   495  	// 0
   496  	//
   497  	// 1
   498  }
   499  

View as plain text