简体   繁体   中英

How to mutate an array of integers in-place in swift through filtering

One can filter an array like this in swift:

var numbers = Array(1...1000000)
numbers = numbers.filter( { return $0 % 2 == 0  } ) 

Is it possible to filter and avoid the copy operation, that occurs when the filtering is done, eg mutating the original array.

In a similar way to this pseudocode: numbers.MutablefilterOperation({ return $0 % 2 == 0})

In C++ the equvivalent to what is going on in Swift above would be:

std::vector<int> originalNumbers(1000000);
std::vector<int> newNumbers;
std::copy_if (originalNumbers.begin(), originalNumbers.end(), std::back_inserter(newNumbers), [](int i) { return i % 2 == 0 } );

What I would like to achieve for performance reasons:

std::vector<int> originalNumbers(1000000);
auto pos = std::remove_if(originalNumbers.begin(), originalNumbers.end(), [](int x) { return x % 2 == 0; });
originalNumbers.erase(pos, originalNumbers.end());

This implementation should do the filtering without having to make a temporary copy of the entire array in the process (unless a copy of it is referenced by another variable, see "Copy on Write")

extension Array {
    mutating func filterInPlace(isIncluded: (Element) throws -> Bool) rethrows {
        var writeIndex = self.startIndex
        for readIndex in self.indices {
            let element = self[readIndex]
            let include = try isIncluded(element)
            if include {
                if writeIndex != readIndex {
                    self[writeIndex] = element
                }
                writeIndex = self.index(after: writeIndex)
            }
        }
        self.removeLast(self.distance(from: writeIndex, to: self.endIndex))
    }
}

// example:
var arr = [6,2,6,5,2,5,6,2,2,1,6,7,3]
arr.filterInPlace { $0 % 2 == 1 }
print(arr) // [5, 5, 1, 7, 3]

Any Array is a struct which is a value type. It can't be mutated, only copied or replaced.

Structures and Enumerations Are Value Types

If you want a reference type you'll have to use a class to implement your own type of array which is mutable - or use Foundation's NSMutableArray .

If you want to simplify the code so you can call it as a mutating function you could do it with an extension :

extension Array {
  // the mutating function should be named "filter" but 
  // this is already taken by the non-mutating function
  mutating func filtering(_ isIncluded: (Element) throws -> Bool) rethrows -> [Element] {
    try self = self.filter(isIncluded)
    return self
  }
}

var numbers = Array(1...10)
numbers.filtering( { return $0 % 2 == 0  } )
print(numbers) // "[2, 4, 6, 8, 10]\n"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM