9

I have an array as a property in a class.

Class Custom {
 let objArray: [CustomClass]
}

I want to remove some items in objArray in a range. So I have done below

let newVar  = objArray[1...3]

new objects are correctly removed but return value is in newVar since array is value type how I can make the original reflect the same.

Below code gets Index out of bounds as the indexes incremented

for i in 1...3 {

  objArray.remove(at: 1)
}

======

What is the best approach for the above issue.

Any hint in right direction would be highly appreciated.

Sapphire_Brick
  • 1,560
  • 12
  • 26
Ekra
  • 3,241
  • 10
  • 41
  • 61
  • You are removing from `1st` index every time. And you can not remove object from array in loop after first time removing array count will be change so it will probably crash. – TheTiger Apr 02 '18 at 12:36

4 Answers4

23

Use removeSubrange method of array. Make a valid range by element location and length.

var array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let range = 1...3
array.removeSubrange(range)

print(array)

Output: [1, 5, 6, 7, 8, 9, 10]

Note: Range should be a valid range I mean it should not be out from array.

Here is yours way (by for loop) We can not remove objects by their indexes in a loop because every time object removes array's count and objects indexes will be change so out of range crash can come or you might get a wrong output. So you will have to take help of another array. See below example:-

var array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
var newArray: [Int] = []
let minRange = 1
let maxRange = 3
for i in 0..<array.count {
     if i >= minRange && i <= maxRange {
        /// Avoid
        continue
     }

     newArray.append(array[i])
}

print(newArray)

Output: [1, 5, 6, 7, 8, 9, 10]

TheTiger
  • 13,264
  • 3
  • 57
  • 82
  • Getting this warning --- Legacy Constructor Violation: Swift constructors are preferred over legacy convenience functions. (legacy_constructor – Ekra Apr 02 '18 at 12:49
  • instead of NSMakeRange can we use something swift – Ekra Apr 02 '18 at 12:51
  • Which swift version you are using? Its in `Swift 4.0` – TheTiger Apr 02 '18 at 12:51
  • We have some validators for Swift that's giving the warning to not use NSMakeRange instead use something swift. In normal Xcode u may not receive the same – Ekra Apr 02 '18 at 12:53
  • @Ekra Its pretty easy like you use in for loop. See my edited answer. Sorry for inconvenience. – TheTiger Apr 02 '18 at 12:55
5

If you want to remove items by index in a range you have to inverse the indexes to start with the highest index otherwise you will get the out-of-range exception. Consider also that indexes are zero-based.

That's a safe version which checks also the upper bound of the array.

var array = [1, 2, 3, 4, 5, 6]
for i in (0...3).reversed() where i < array.count {
    array.remove(at: i)
}

print(array) // [5, 6]

You can find a more generic and more efficient solution here

vadian
  • 274,689
  • 30
  • 353
  • 361
  • Very nice, clean and good working solution, I actually prefer yours for it's simplicity and good use of swift syntax. – JoniVR Feb 03 '19 at 13:30
2

This solution also returns the removed values

extension Array {
   /**
    * ## Examples:
    * var arr = [0,1,2,3]
    * arr.remove((0..<2)) // 0,1
    * arr // 2,3
    */
   mutating func remove(_ range: Range<Int>) -> Array {
      let values = Array(self[range])
      self.removeSubrange(range)
      return values
   }
}
Sentry.co
  • 5,355
  • 43
  • 38
0

The issue you are having is that an array index is zero based, which is to say, the first element in an array is accessed bv:

Let firstArrayValue = objArray[0]

So in the case of your for loop, you need to subtact 1 from i to get the proper index value:

for i in 1…3 {

objArray.remove(at: i-1) }

A better way is to loop through the indices by starting at 0. i = 0 will reference the first value in your objArray:

for i in 0...2 {
    objArray.remove(at: i)
}

If you need to remove elements in the middle of the array you must first find their index location then remove. To find the index:

let indexLocation = objArray(indexOf: "Value in Array")

Then remove:

objArray.remove(at: indexLocation)
Martin Muldoon
  • 3,388
  • 4
  • 24
  • 55
  • `objArray.remove(at: i)` will give wrong output. Because after removing first time array count and elements indexes will be change. – TheTiger Apr 02 '18 at 12:43
  • i need to remove the objects from middle of array – Ekra Apr 02 '18 at 12:52
  • I'll update my answer. You have to first locate the index of the elements you want to remove. Then remove them. It's a two step process. – Martin Muldoon Apr 02 '18 at 12:59