Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 1 | Since the introduction of the ` append ` built-in, most of the functionality of the ` container/vector ` package, which was removed in Go 1, can be replicated using ` append ` and ` copy `. |
| 2 | |
| 3 | Here are the vector methods and their slice-manipulation analogues: |
| 4 | |
Lars Walen | 35b2d5e | 2017-03-21 13:54:47 -0700 | [diff] [blame] | 5 | #### AppendVector |
Andrew Stone | 7a268d0 | 2015-03-11 13:12:10 -0400 | [diff] [blame] | 6 | ```go |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 7 | a = append(a, b...) |
| 8 | ``` |
Dave | 8f62fba | 2015-01-08 12:44:59 +1100 | [diff] [blame] | 9 | |
Lars Walen | 35b2d5e | 2017-03-21 13:54:47 -0700 | [diff] [blame] | 10 | #### Copy |
Andrew Stone | 7a268d0 | 2015-03-11 13:12:10 -0400 | [diff] [blame] | 11 | ```go |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 12 | b = make([]T, len(a)) |
| 13 | copy(b, a) |
Jagatheesan Jack | ddb2ccf | 2017-05-14 10:32:01 +0800 | [diff] [blame] | 14 | // or |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 15 | b = append([]T(nil), a...) |
Eugene Russkikh | 3ba038c | 2018-10-02 22:40:17 +0200 | [diff] [blame] | 16 | // or |
| 17 | b = append(a[:0:0], a...) // See https://github.com/go101/go101/wiki |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 18 | ``` |
Dave | 8f62fba | 2015-01-08 12:44:59 +1100 | [diff] [blame] | 19 | |
Lars Walen | 35b2d5e | 2017-03-21 13:54:47 -0700 | [diff] [blame] | 20 | #### Cut |
Andrew Stone | 7a268d0 | 2015-03-11 13:12:10 -0400 | [diff] [blame] | 21 | ```go |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 22 | a = append(a[:i], a[j:]...) |
| 23 | ``` |
Dave | 8f62fba | 2015-01-08 12:44:59 +1100 | [diff] [blame] | 24 | |
Lars Walen | 35b2d5e | 2017-03-21 13:54:47 -0700 | [diff] [blame] | 25 | #### Delete |
Andrew Stone | 7a268d0 | 2015-03-11 13:12:10 -0400 | [diff] [blame] | 26 | ```go |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 27 | a = append(a[:i], a[i+1:]...) |
| 28 | // or |
| 29 | a = a[:i+copy(a[i:], a[i+1:])] |
| 30 | ``` |
Dave | 8f62fba | 2015-01-08 12:44:59 +1100 | [diff] [blame] | 31 | |
Maxim Litvinov | 3941028 | 2018-12-31 09:12:50 +0200 | [diff] [blame] | 32 | #### Delete without preserving order |
Maxim Litvinov | 3ddd170 | 2018-12-31 09:11:06 +0200 | [diff] [blame] | 33 | ```go |
Maxim Litvinov | 3941028 | 2018-12-31 09:12:50 +0200 | [diff] [blame] | 34 | a[i] = a[len(a)-1] |
Maxim Litvinov | 3ddd170 | 2018-12-31 09:11:06 +0200 | [diff] [blame] | 35 | a = a[:len(a)-1] |
Maxim Litvinov | 3ddd170 | 2018-12-31 09:11:06 +0200 | [diff] [blame] | 36 | |
Maxim Litvinov | 3941028 | 2018-12-31 09:12:50 +0200 | [diff] [blame] | 37 | ``` |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 38 | **NOTE** If the type of the element is a _pointer_ or a struct with pointer fields, which need to be garbage collected, the above implementations of ` Cut ` and ` Delete ` have a potential _memory leak_ problem: some elements with values are still referenced by slice ` a ` and thus can not be collected. The following code can fix this problem: |
Dave | 8f62fba | 2015-01-08 12:44:59 +1100 | [diff] [blame] | 39 | > **Cut** |
Andrew Stone | 7a268d0 | 2015-03-11 13:12:10 -0400 | [diff] [blame] | 40 | ```go |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 41 | copy(a[i:], a[j:]) |
Dave Day | 0d6986a | 2014-12-10 15:02:18 +1100 | [diff] [blame] | 42 | for k, n := len(a)-j+i, len(a); k < n; k++ { |
| 43 | a[k] = nil // or the zero value of T |
David Deng | 2b4bafd | 2015-04-28 08:01:53 -0700 | [diff] [blame] | 44 | } |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 45 | a = a[:len(a)-j+i] |
| 46 | ``` |
Dave | 8f62fba | 2015-01-08 12:44:59 +1100 | [diff] [blame] | 47 | |
| 48 | > **Delete** |
Andrew Stone | 7a268d0 | 2015-03-11 13:12:10 -0400 | [diff] [blame] | 49 | ```go |
Andrew Stone | 55f404a | 2015-03-11 13:10:56 -0400 | [diff] [blame] | 50 | copy(a[i:], a[i+1:]) |
| 51 | a[len(a)-1] = nil // or the zero value of T |
| 52 | a = a[:len(a)-1] |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 53 | ``` |
Dave | 8f62fba | 2015-01-08 12:44:59 +1100 | [diff] [blame] | 54 | |
| 55 | > **Delete without preserving order** |
Andrew Stone | 7a268d0 | 2015-03-11 13:12:10 -0400 | [diff] [blame] | 56 | ```go |
alandonovan | bddf9a5 | 2015-07-27 16:15:39 -0400 | [diff] [blame] | 57 | a[i] = a[len(a)-1] |
| 58 | a[len(a)-1] = nil |
| 59 | a = a[:len(a)-1] |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 60 | ``` |
| 61 | |
Lars Walen | 35b2d5e | 2017-03-21 13:54:47 -0700 | [diff] [blame] | 62 | #### Expand |
Andrew Stone | 7a268d0 | 2015-03-11 13:12:10 -0400 | [diff] [blame] | 63 | ```go |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 64 | a = append(a[:i], append(make([]T, j), a[i:]...)...) |
| 65 | ``` |
Dave | 8f62fba | 2015-01-08 12:44:59 +1100 | [diff] [blame] | 66 | |
Lars Walen | 35b2d5e | 2017-03-21 13:54:47 -0700 | [diff] [blame] | 67 | #### Extend |
Andrew Stone | 7a268d0 | 2015-03-11 13:12:10 -0400 | [diff] [blame] | 68 | ```go |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 69 | a = append(a, make([]T, j)...) |
| 70 | ``` |
| 71 | |
Lars Walen | 35b2d5e | 2017-03-21 13:54:47 -0700 | [diff] [blame] | 72 | #### Insert |
Andrew Stone | 7a268d0 | 2015-03-11 13:12:10 -0400 | [diff] [blame] | 73 | ```go |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 74 | a = append(a[:i], append([]T{x}, a[i:]...)...) |
| 75 | ``` |
| 76 | **NOTE** The second ` append ` creates a new slice with its own underlying storage and copies elements in ` a[i:] ` to that slice, and these elements are then copied back to slice ` a ` (by the first ` append `). The creation of the new slice (and thus memory garbage) and the second copy can be avoided by using an alternative way: |
Dave | 8f62fba | 2015-01-08 12:44:59 +1100 | [diff] [blame] | 77 | > **Insert** |
Andrew Stone | 7a268d0 | 2015-03-11 13:12:10 -0400 | [diff] [blame] | 78 | ```go |
Ian Lance Taylor | c69144a | 2018-12-27 14:17:54 -0800 | [diff] [blame] | 79 | s = append(s, 0 /* use the zero value of the element type */) |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 80 | copy(s[i+1:], s[i:]) |
| 81 | s[i] = x |
| 82 | ``` |
| 83 | |
Lars Walen | 35b2d5e | 2017-03-21 13:54:47 -0700 | [diff] [blame] | 84 | #### InsertVector |
Andrew Stone | 7a268d0 | 2015-03-11 13:12:10 -0400 | [diff] [blame] | 85 | ```go |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 86 | a = append(a[:i], append(b, a[i:]...)...) |
| 87 | ``` |
Dave | 8f62fba | 2015-01-08 12:44:59 +1100 | [diff] [blame] | 88 | |
Maxim Litvinov | 9ade7aa | 2018-12-31 09:03:58 +0200 | [diff] [blame] | 89 | #### Push |
Andrew Stone | 7a268d0 | 2015-03-11 13:12:10 -0400 | [diff] [blame] | 90 | ```go |
Maxim Litvinov | 9ade7aa | 2018-12-31 09:03:58 +0200 | [diff] [blame] | 91 | a = append(a, x) |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 92 | ``` |
Dave | 8f62fba | 2015-01-08 12:44:59 +1100 | [diff] [blame] | 93 | |
Maxim Litvinov | f1e0706 | 2018-12-31 09:00:22 +0200 | [diff] [blame] | 94 | #### Pop |
Helmut Kemper | 9d79a99 | 2017-06-28 10:34:07 -0300 | [diff] [blame] | 95 | ```go |
Sergey Dobrodey | 922463f | 2017-10-10 13:19:56 +0300 | [diff] [blame] | 96 | x, a = a[len(a)-1], a[:len(a)-1] |
Helmut Kemper | 9d79a99 | 2017-06-28 10:34:07 -0300 | [diff] [blame] | 97 | ``` |
| 98 | |
Axel Wagner | d4a1573 | 2018-02-04 08:09:46 +0100 | [diff] [blame] | 99 | #### Push Front/Unshift |
Andrew Stone | 7a268d0 | 2015-03-11 13:12:10 -0400 | [diff] [blame] | 100 | ```go |
Joshua Mervine | 0ab6581 | 2015-03-10 22:28:12 -0700 | [diff] [blame] | 101 | a = append([]T{x}, a...) |
Joshua Mervine | d37365d | 2015-03-10 22:25:29 -0700 | [diff] [blame] | 102 | ``` |
| 103 | |
Maxim Litvinov | 9ade7aa | 2018-12-31 09:03:58 +0200 | [diff] [blame] | 104 | #### Pop Front/Shift |
| 105 | ```go |
| 106 | x, a = a[0], a[1:] |
| 107 | ``` |
| 108 | |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 109 | ## Additional Tricks |
| 110 | ### Filtering without allocating |
| 111 | |
| 112 | This trick uses the fact that a slice shares the same backing array and capacity as the original, so the storage is reused for the filtered slice. Of course, the original contents are modified. |
| 113 | |
Andrew Stone | 7a268d0 | 2015-03-11 13:12:10 -0400 | [diff] [blame] | 114 | ```go |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 115 | b := a[:0] |
| 116 | for _, x := range a { |
Dave Day | 0d6986a | 2014-12-10 15:02:18 +1100 | [diff] [blame] | 117 | if f(x) { |
| 118 | b = append(b, x) |
| 119 | } |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 120 | } |
Kamil Kisiel | 1f35091 | 2015-04-20 13:46:59 -0700 | [diff] [blame] | 121 | ``` |
| 122 | |
| 123 | ### Reversing |
| 124 | |
| 125 | To replace the contents of a slice with the same elements but in reverse order: |
| 126 | ```go |
David Deng | 2b4bafd | 2015-04-28 08:01:53 -0700 | [diff] [blame] | 127 | for i := len(a)/2-1; i >= 0; i-- { |
Kamil Kisiel | 1f35091 | 2015-04-20 13:46:59 -0700 | [diff] [blame] | 128 | opp := len(a)-1-i |
| 129 | a[i], a[opp] = a[opp], a[i] |
| 130 | } |
lemmi | db4a388 | 2015-06-26 17:43:13 +0200 | [diff] [blame] | 131 | ``` |
| 132 | The same thing, except with two indices: |
| 133 | ```go |
| 134 | for left, right := 0, len(a)-1; left < right; left, right = left+1, right-1 { |
| 135 | a[left], a[right] = a[right], a[left] |
| 136 | } |
Eugene Russkikh | f2fde5f | 2017-11-05 00:53:09 +0100 | [diff] [blame] | 137 | ``` |
| 138 | |
| 139 | ### Shuffling |
| 140 | |
| 141 | Fisher–Yates algorithm: |
Andrew Stone | ad72ba5 | 2018-05-04 13:34:49 -0700 | [diff] [blame] | 142 | |
| 143 | > Since go1.10, this is available at [math/rand.Shuffle](https://godoc.org/math/rand#Shuffle) |
| 144 | |
Eugene Russkikh | f2fde5f | 2017-11-05 00:53:09 +0100 | [diff] [blame] | 145 | ```go |
| 146 | for i := len(a) - 1; i > 0; i-- { |
| 147 | j := rand.Intn(i + 1) |
| 148 | a[i], a[j] = a[j], a[i] |
| 149 | } |
Jeremy Loy | 3485782 | 2018-08-13 09:44:20 -0400 | [diff] [blame] | 150 | ``` |
| 151 | |
| 152 | ### Batching with minimal allocation |
| 153 | |
| 154 | Useful if you want to do batch processing on large slices. |
| 155 | |
| 156 | ```go |
| 157 | actions := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} |
| 158 | batchSize := 3 |
| 159 | var batches [][]int |
| 160 | |
| 161 | for batchSize < len(actions) { |
| 162 | actions, batches = actions[batchSize:], append(batches, actions[0:batchSize:batchSize]) |
| 163 | } |
| 164 | batches = append(batches, actions) |
| 165 | ``` |
| 166 | |
| 167 | Yields the following: |
| 168 | ```go |
| 169 | [[0 1 2] [3 4 5] [6 7 8] [9]] |
Meng Zhuo | b9434c8 | 2018-09-28 14:32:34 +0800 | [diff] [blame] | 170 | ``` |
| 171 | |
| 172 | ### In-place deduplicate (comparable) |
| 173 | |
| 174 | ```go |
| 175 | import "sort" |
| 176 | |
| 177 | in := []int{3,2,1,4,3,2,1,4,1} // any item can be sorted |
| 178 | sort.Ints(in) |
| 179 | j := 0 |
| 180 | for i := 1; i < len(in); i++ { |
| 181 | if in[j] == in[i] { |
| 182 | continue |
| 183 | } |
| 184 | j++ |
Thomas Mangin | bc97e75 | 2018-12-31 17:41:42 +0000 | [diff] [blame] | 185 | // preserve the original data |
| 186 | // in[i], in[j] = in[j], in[i] |
| 187 | // only set what is required |
| 188 | in[j] = in[i] |
Meng Zhuo | b9434c8 | 2018-09-28 14:32:34 +0800 | [diff] [blame] | 189 | } |
Adam DiCarlo | d8e8a7f | 2018-10-18 13:10:48 -0700 | [diff] [blame] | 190 | result := in[:j+1] |
| 191 | fmt.Println(result) // [1 2 3 4] |
Andrew Gerrand | 5bc444d | 2014-12-10 11:35:11 +1100 | [diff] [blame] | 192 | ``` |