How to remove items from a slice while ranging over it?

Go

Go Problem Overview


What is the best way to remove items from a slice while ranging over it?

For example:

type MultiDataPoint []*DataPoint

func (m MultiDataPoint) Json() ([]byte, error) {
	for i, d := range m {
		err := d.clean()
		if ( err != nil ) {
			//Remove the DP from m
		}
	}
	return json.Marshal(m)
}

Go Solutions


Solution 1 - Go

As you have mentioned elsewhere, you can allocate new memory block and copy only valid elements to it. However, if you want to avoid the allocation, you can rewrite your slice in-place:

i := 0 // output index
for _, x := range s {
    if isValid(x) {
        // copy and increment index
        s[i] = x
        i++
    }
}
// Prevent memory leak by erasing truncated values 
// (not needed if values don't contain pointers, directly or indirectly)
for j := i; j < len(s); j++ {
    s[j] = nil
}
s = s[:i]

Full example: http://play.golang.org/p/FNDFswPeDJ

Note this will leave old values after index i in the underlying array, so this will leak memory until the slice itself is garbage collected, if values are or contain pointers. You can solve this by setting all values to nil or the zero value from i until the end of the slice before truncating it.

Solution 2 - Go

I know its answered long time ago but i use something like this in other languages, but i don't know if it is the golang way.

Just iterate from back to front so you don't have to worry about indexes that are deleted. I am using the same example as Adam.

m = []int{3, 7, 2, 9, 4, 5}

for i := len(m)-1; i >= 0; i-- {
	if m[i] < 5 {
		m = append(m[:i], m[i+1:]...)
	}
}

Solution 3 - Go

There might be better ways, but here's an example that deletes the even values from a slice:

m := []int{1,2,3,4,5,6}

deleted := 0
for i := range m {
    j := i - deleted
    if (m[j] & 1) == 0 {
        m = m[:j+copy(m[j:], m[j+1:])]
        deleted++
    } 
}

Note that I don't get the element using the i, d := range m syntax, since d would end up getting set to the wrong elements once you start deleting from the slice.

Solution 4 - Go

One other option is to use a normal for loop using the length of the slice and subtract 1 from the index each time a value is removed. See the following example:

m := []int{3, 7, 2, 9, 4, 5}

for i := 0; i < len(m); i++ {
	if m[i] < 5 {
		m = append(m[:i], m[i+1:]...)
		i-- // -1 as the slice just got shorter
	}
}

I don't know if len() uses enough resources to make any difference but you could also run it just once and subtract from the length value too:

m := []int{3, 7, 2, 9, 4, 5}

for i, s := 0, len(m); i < s; i++ {
	if m[i] < 5 {
		m = append(m[:i], m[i+1:]...)
		s--
		i--
	}
}

Solution 5 - Go

Here is a more idiomatic Go way to remove elements from slices.

temp := s[:0]
for _, x := range s {
	if isValid(x) {
		temp = append(temp, x)
	}
}
s = temp

Playground link: https://play.golang.org/p/OH5Ymsat7s9

Note: The example and playground links are based upon @tomasz's answer https://stackoverflow.com/a/20551116/12003457

Solution 6 - Go

Something like:

m = append(m[:i], m[i+1:]...)

Solution 7 - Go

You don't even need to count backwards but you do need to check that you're at the end of the array where the suggested append() will fail. Here's an example of removing duplicate positive integers from a sorted list:

// Remove repeating numbers
numbers := []int{1, 2, 3, 3, 4, 5, 5}
log.Println(numbers)
for i, numbersCount, prevNum := 0, len(numbers), -1; i < numbersCount; numbersCount = len(numbers) {
	if numbers[i] == prevNum {
		if i == numbersCount-1 {
			numbers = numbers[:i]
		} else {
			numbers = append(numbers[:i], numbers[i+1:]...)

		}
		continue
	}
	prevNum = numbers[i]
	i++

}
log.Println(numbers)

Playground: https://play.golang.org/p/v93MgtCQsaN

Solution 8 - Go

I just implement a method which removes all nil elements in slice.

And I used it to solve a leetcode problems, it works perfectly.

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
 func removeNil(lists *[]*ListNode) {
	for i := 0; i < len(*lists); i++ {
		if (*lists)[i] == nil {
			*lists = append((*lists)[:i], (*lists)[i+1:]...)
			i--
		}
	}
}

Solution 9 - Go

Try Sort and Binary search.

Example:

package main

import (
	"fmt"
	"sort"
)

func main() {
	// Our slice.
	s := []int{3, 7, 2, 9, 4, 5}
	
	// 1. Iterate over it.
	for i, v := range s {
		func(i, v int) {}(i, v)
	}
	
	// 2. Sort it. (by whatever condition of yours)
	sort.Slice(s, func(i, j int) bool {
		return s[i] < s[j]
	})
	
	// 3. Cut it only once.
	i := sort.Search(len(s), func(i int) bool { return s[i] >= 5 })
	s = s[i:]
	
	// That's it!
	fmt.Println(s) // [5 7 9]
}

https://play.golang.org/p/LnF6o0yMJGT

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionKyle BrandtView Question on Stackoverflow
Solution 1 - GotomaszView Answer on Stackoverflow
Solution 2 - GoautlunaticView Answer on Stackoverflow
Solution 3 - GoMichaelView Answer on Stackoverflow
Solution 4 - GoAdam BView Answer on Stackoverflow
Solution 5 - GoVidya Sagar KalvakuntaView Answer on Stackoverflow
Solution 6 - GoCharlie AndrewsView Answer on Stackoverflow
Solution 7 - GodmkcView Answer on Stackoverflow
Solution 8 - Go姜鹄_View Answer on Stackoverflow
Solution 9 - GoCoconutView Answer on Stackoverflow