Alex Pliutau
Alex Pliutau

Reputation: 21957

How to remove element of struct array in loop in golang

Problem

I have array of structs:

type Config struct {
  Applications []Application
}

Note: Config - is a struct for json.Decode.

config = new(Config)
_ = decoder.Decode(&config)

In loop I have some condition and element deletion by key.

for i, application := range config.Applications {
  if i == 1 {
    config.Applications = _removeApplication(i, config.Applications)
  }
}

func _removeApplication(i int, list []Application) []Application {
  if i < len(list)-1 {
    list = append(list[:i], list[i+1:]...)
  } else {
    log.Print(list[i].Name)
    list = list[:i]
  }

  return list
}

But always I have "out of range" error. What is the best way to delete element by key from array of structs?

Upvotes: 24

Views: 36166

Answers (5)

SHIVA BANDARI
SHIVA BANDARI

Reputation: 41

To remove a particular slice from a struct we need to run a for-loop to find that particular slice and delete it

for example:

type variable struct {
Id      int    `json:"id"`
Message string `json:"message"`
}

var mssg = []variable{
{Id: 1, Message: "success"},
{Id: 2, Message: "failed"}
}
for i, a := range mssg {
    if a.Message == "success" {
        mssg = append(mssg[:i], mssg[i+1:]...)
        fmt.Println(mssg)
    }

Upvotes: 0

Martin Prestone
Martin Prestone

Reputation: 4703

I think the simple way is

var (
  slice = []int{1,2,3,4,5}
  pos int
)
    for _, i := range slice {
        if i == 3 {
            slice = append(slice[:pos], slice[pos+1:]...)
            if pos > 0 {
                pos = pos - 1
            }
            continue
        }
        pos++
    }

here is... https://play.golang.org/p/pK3B5Mii9k

Upvotes: 1

treigerm
treigerm

Reputation: 41

This question is a bit older but I haven't found another answer on StackOverflow which mentions the following trick from the Slice Tricks to filter a list:

b := a[:0]
for _, x := range a {
    if f(x) {
        b = append(b, x)
    }
}

So in this case a function which deletes certain elements could look like this:

func removeApplications(apps []Applications) []Applications {
    filteredApps := apps[:0]
    for _, app := apps {
        if !removeApp {
            filteredApps = append(filteredApps, app)
        }
    }
    return filteredApps
}

Upvotes: 4

icza
icza

Reputation: 417682

Quoting from the Slice Tricks page deleting the element at index i:

a = append(a[:i], a[i+1:]...)
// or
a = a[:i+copy(a[i:], a[i+1:])]

Note that if you plan to delete elements from the slice you're currently looping over, that may cause problems. And it does if the element you remove is the current one (or a previous element already looped over) because after the deletion all subsequent elements are shifted, but the range loop does not know about this and will still increment the index and you skip one element.

You can avoid this by using a downward loop:

for i := len(config.Applications) - 1; i >= 0; i-- {
    application := config.Applications[i]
    // Condition to decide if current element has to be deleted:
    if haveToDelete {
        config.Applications = append(config.Applications[:i],
                config.Applications[i+1:]...)
    }
}

Upvotes: 47

gdg
gdg

Reputation: 587

You are getting this error because you are doing a loop over a slice with an inital range of X length that became X-n because you remove some elements during loop.

If you want to delete an item at a specific index from a slice, you can do it this way:

sliceA = append(sliceA[:indexOfElementToRemove], sliceA[indexOfElementToRemove+1:]...)

Upvotes: 7

Related Questions