I have a simple 2D array with multiple rows. I want to be able to completely delete a row - not just make it blank/empty.
I have a button that removes the selected row. I've tried the following methods, but all of them leave an empty row in the array, as opposed to completely removing the row. (in this example the first row, but it needs to work on any row selected).
array[0] = [];
array[0].splice();
array[0].length = 0;
array[0].splice(0);
array[0] = null;
Array.splice() is the method you need.
package
{
import flash.display.Sprite;
public class Main extends Sprite
{
private var grid:Array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
public function Main()
{
trace(this.grid); // 1,2,3,4,5,6,7,8,9
grid.splice(0, 1);
trace(this.grid); // 4,5,6,7,8,9
}
}
}
Related
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.
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]
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
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
}
}
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)
In my project, I have two arrays of Ints
In pseduocode;
var existingOrders:[ExistingOrder] ...
var completedOrders:[CompletedOrder] ..
protocol ValueProtocol {
var value: Int { get set }
}
class ExistingOrder: ValueProtocol {
var value: Int = 0
}
class CompletedOrder: ValueProtocol {
var value: Int = 0
}
Yes, I know that functionality is the same; but I'm needing it to be two classes for a reason that is outside the scope of this question.
One function I'm writing I need to transfer one order from Existing Order to Completed.
In my code I loop through all the values I want to transfer, then transfer them
for (index, item) in self.completedOrders.enumerated() {
item.value = Die.roll()
self.transfer(index: index, destination: .existingOrder)
}
The transfer function moves it from existingOrder -> completedOrder and vice versa.
The problem is this:
When it steps through the array shown above it'll pop an object during the transfer; and now the size of the array has changed and is no longer correct.
Visually stepping through it, it looks like this;
ie;
// stepping through the for-loop
existingOrders = [3,2]
transfer > index 0 (Integer value of 3) -> completed
existingOrders = [2]
transfer > index 1 .. crash; because the size of the array isn't correct any more.
So the for-loop is going through each item it needs to transfer, but because the transfer itself is amending the for-loop it changes the contents of said array and causes a crash.
The reason I use an index is because sometimes I want a specific item within the existing orders to be transferred.
I'm wondering how I can avoid this issue?
Thanks
Loop through your array in reverse order. That way, the earlier indices will still be valid as the later items are removed:
for (index, item) in self.completedOrders.enumerated().reversed() {
item.value = Die.roll()
self.transfer(index: index, destination: .existingOrder)
}
Here's an example of how this works with an array of numbers in which we remove the odds ones:
var numbers = Array(1...10) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
var odds = [Int]()
for (index, number) in numbers.enumerated().reversed() {
if number % 2 == 1 {
odds.insert(number, at: 0)
numbers.remove(at: index)
}
}
print(numbers) // [2, 4, 6, 8, 10]
print(odds) // [1, 3, 5, 7, 9]
This question already has answers here:
In Swift, an efficient function that separates an array into 2 arrays based on a predicate
(7 answers)
Closed 6 months ago.
Problem
Given an array of values how can I split it into sub-arrays made of elements that are equal?
Example
Given this array
let numbers = [1, 1, 1, 3, 3, 4]
I want this output
[[1,1,1], [3, 3], [4]]
What I am NOT looking for
A possible way of solving this would be creating some sort of index to indicate the occurrences of each element like this.
let indexes = [1:3, 3:2, 4:1]
And finally use the index to rebuild the output array.
let subsequences = indexes.sort { $0.0.0 < $0.1.0 }.reduce([Int]()) { (res, elm) -> [Int] in
return res + [Int](count: elm.1, repeatedValue: elm.0)
}
However with this solution I am losing the original values. Of course in this case it's not a big problem (an Int value is still and Inteven if recreated) but I would like to apply this solution to more complex data structures like this
struct Starship: Equatable {
let name: String
let warpSpeed: Int
}
func ==(left:Starship, right:Starship) -> Bool {
return left.warpSpeed == right.warpSpeed
}
Final considerations
The function I am looking for would be some kind of reverse of flatten(), infact
let subsequences: [[Int]] = [[1,1,1], [3, 3], [4]]
print(Array(subsequences.flatten())) // [1, 1, 1, 3, 3, 4]
I hope I made myself clear, let me know should you need further details.
// extract unique numbers using a set, then
// map sub-arrays of the original arrays with a filter on each distinct number
let numbers = [1, 1, 1, 3, 3, 4]
let numberGroups = Set(numbers).map{ value in return numbers.filter{$0==value} }
print(numberGroups)
[EDIT] changed to use Set Initializer as suggested by Hamish
[EDIT2] Swift 4 added an initializer to Dictionary that will do this more efficiently:
let numberGroups = Array(Dictionary(grouping:numbers){$0}.values)
For a list of objects to be grouped by one of their properties:
let objectGroups = Array(Dictionary(grouping:objects){$0.property}.values)
If you could use CocoaPods/Carthage/Swift Package Manager/etc. you could use packages like oisdk/SwiftSequence which provides the group() method:
numbers.lazy.group()
// should return a sequence that generates [1, 1, 1], [3, 3], [4].
or UsrNameu1/TraverSwift which provides groupBy:
groupBy(SequenceOf(numbers), ==)
If you don't want to add external dependencies, you could always write an algorithm like:
func group<S: SequenceType where S.Generator.Element: Equatable>(seq: S) -> [[S.Generator.Element]] {
var result: [[S.Generator.Element]] = []
var current: [S.Generator.Element] = []
for element in seq {
if current.isEmpty || element == current[0] {
current.append(element)
} else {
result.append(current)
current = [element]
}
}
result.append(current)
return result
}
group(numbers)
// returns [[1, 1, 1], [3, 3], [4]].
Let's assume that you have an unsorted array of items. You will need to sort the initial array then you will have something like this:
[1, 1, 1, 3, 3, 4]
After that you will initialize two arrays: one for storing arrays and another one to use it as a current array.
Loop through the initial array and:
if the current value isn't different from the last one, push it to the current array
otherwise push the current array to the first one then empty the current array.
Hope it helps!
Worth mentioning, using Swift Algorithms this is now a one-liner:
import Algorithms
let numbers = [1, 1, 1, 3, 3, 4]
let chunks: [[Int]] = numbers.chunked(by: ==).map { .init($0) }
print(chunks) // [[1, 1, 1], [3, 3], [4]]
Below I am trying to fetch the i'th element of the ArraySlice draggignFan. The code builds fine (no warnings) but the program dies at runtime on the line where I try to index the slice like a normal array:
var draggingFan : ArraySlice<Card>?
...
if let draggingFan = draggingFan {
for i in 1 ..< draggingFan.count {
let card = draggingFan[i] // EXECUTION ERROR HERE
...
}
}
According to the docs there is a first and last method (which I use elsewhere with no problem). So how do I index an ArraySlice in Swift? (Note: I am intentionally skipping the 0'th index in the slice -- that's needed elsewhere).
The indices of the ArraySlice still match those of the original array. In your case, you are accessing index 1 which is not in your slice. If you offset the index by draggingFan.startIndex it will work:
if let draggingFan = draggingFan {
for i in 1 ..< draggingFan.count {
let card = draggingFan[draggingFan.startIndex + i]
...
}
}
Alternatively:
if let draggingFan = draggingFan {
for i in draggingFan.startIndex + 1 ..< draggingFan.endIndex {
let card = draggingFan[i]
...
}
}
This will access the values from the second element in the slice to the last element in the slice:
let original = [1,2,3,4,5,6] // Int array to demonstrate
var draggingFan : ArraySlice<Int>?
draggingFan = original[1...4] // create the slice
if let draggingFan = draggingFan {
// so there's no errors just slice the slice and iterate over it
for i in draggingFan[(draggingFan.startIndex+1)..<draggingFan.endIndex] {
print(i, terminator: ", ")
}
}
Output:
3, 4, 5,
The reason you are having this problem is that the slice maintains the original index numbers of the sequence you got it from. Thus, element 1 is not in this slice.
For example, consider this code:
let arr = [1,2,3,4,5,6,7,8,9]
let slice = arr[2...5]
Now what is slice[1]? It isn't 4, even though that is the second thing in the slice. It's 2, because the slice still points into the original array. In other words, slice[1] is out of the slice's range! That is why you're getting a runtime error.
What to do? Well, the actual indexes of the slice are its indices. That is what you want to cycle thru. But... You don't want the first element pointed to by the slice. So you need to advance the startIndex of the range you're going to iterate through. Thus:
if let draggingFan = draggingFan {
var ixs = draggingFan.indices
ixs.startIndex = ixs.startIndex.advancedBy(1)
for i in ixs {
// ... now your code will work ...
}
}
However, in my view, there's no need to index the slice at all, and you shouldn't be doing so. You should cycle through the slice itself, not thru its indexes. You have this:
for i in 1 ..< draggingFan.count
But that is much like saying
for aCard in draggingFan
...except that you want to drop the first element of the slice. Then drop it! Say this:
for aCard in draggingFan.dropFirst()
To see that this will work, try this in a playground:
let arr = [1,2,3,4,5,6,7,8,9]
let slice = arr[2...5]
for anInt in slice.dropFirst() {
print(anInt) // 4, 5, 6
}
As you can see, we are cycling through exactly the desired elements, with no reference to index numbers at all.
To iterate over the elements in the slice:
draggingFan?.forEach({ (element)
...
})
As far as I know, the get a specific element, it needs to be converted back to an array e.g.
let draggingFanArray = Array(draggingFan!)
Here's the playground code I used to toy around with various scenarios:
import Cocoa
var a: Array<Int>?
var b: ArraySlice<Int>?
a = [1, 2, 3, 4, 5, 6, 7]
b = a![3...5]
let count = b!.count
b!.forEach({ (element) in
print("\(element)")
})
let c = Array(b!)
print(c[2])
edit ArraySlice extension though:
extension ArraySlice {
func elementAtIndex(index: Int)->AnyObject?{
return Array(self)[index] as? AnyObject
}
}
If I have an array:
var arr = [1, 2, 3, 4, 5, 6, 7] // [1, 2, 3, 4, 5, 6, 7]
And I take a slice of the array:
let slice = arr[3..<arr.count] // [4, 5, 6, 7]
This slice will have a startIndex of 3, which means that indexing starts at 3 and ends at 6.
Now if I want a slice containing everything but the first element, I can use the dropFirst() method:
let sliceMinusFirst = slice.dropFirst() // [5, 6, 7]
And at this point, sliceMinusFirst has a startIndex of 4, which means my indexes range from 4 to 6.
Now if I wish to iterate over these to do something with the items, I can do the following:
for item in sliceMinusFirst {
print(item)
}
Alternatively, I can do it with forEach:
sliceMinusFirst.forEach { item in
print(item)
}
By using these forms of iteration, the fact that the startIndex is nonzero doesn't even matter, because I don't use the indices directly. And it also doesn't matter that, after taking a slice, I wanted to drop the first item. I was able to do that easily. I could have even done that at the time I wanted to do the iteration:
slice.dropFirst().forEach { item in
print(item)
}
Here I dropped the first item from the original slice, without creating any intermediate variables.
Remember that if you need to actually use the index, you're probably doing something wrong. And if you genuinely do need the index, make sure you understand what's going on.
Also if you want to get back to zero-based indexing once you make a slice, you can create an array from your slice:
let sliceArray = Array(slice) // [4, 5, 6, 7]
sliceArray.startIndex // 0
Ok, so I have a array like this.
myArray:Array []
myArray.push(Object) // x 4.
I have 4 objects in my array. Then I splice the second one. Leaving me with a array that looks like this. 1,3,4. Now... I would like to have this array look like this: 1,2,3. I Would like the 3 & 4 to simple switch place to one less from their own postion.
I know it's possible, I had a friend do it for me some years ago. How do I do this?
i think you are failing when using splice. splice and slice is different methods for array slice not changes the original array but returns new array with sliced gap
example
var arr:Array = ["Cat","Dog", "Mouse", "Cow"];
trace(arr.slice(1,3));// output : Dog,Mouse
trace(arr.slice(2));//output: Mouse,Cow
trace(arr) //output: Cat,Dog,Mouse,Cow
so when you call arr[3] you are getting Cow but your array didnt change at all
splice modifies your original array
var arr:Array = ["Cat","Dog", "Mouse", "Cow"];
arr.splice(1,1); // output: 0->Cat,1->Mouse,2->Cow
I would like to have this array look like this: 1,2,3
So...just pop() the last element:
myArray:Array = [1, 2, 3, 4];
myArray.pop();
trace(myArray); // Prints: 1,2,3
I Would like the 3 & 4 to simple switch place to one less from their own postion.
Umm...that's exactly what deleting the second element already did...
If the end result you want is that the second element be placed at the end, that is:
Before: [1, 2, 3, 4] --> After [1, 3, 4, 2]
Then you can do that in two steps:
var spliced = myArray.splice(1, 1); // Remove the second element and return it
myArray.push(spliced); // Push it to the end of the array
Technically you could do this in one step, since the splice will happen first:
myArray.push(myArray.splice(1, 1));
I ran your pseudo code ->
Here is the result:
[object Cat],[object Mouse],[object Cow]
There is no index when tracing.. another point, I some code
trace(arr[0] , arr[1], arr[2]);
And I still get
[object Cat],[object Mouse],[object Cow]
The indexes are fine automatically in AS3.
package regression
{
import flash.display.Sprite;
/**
* ...
* #author Arthur Wulf White
*/
public class Check_array_index_test_1 extends Sprite
{
public function Check_array_index_test_1()
{
var arr: Array = new Array();
arr.push(new Cat, new Dog, new Mouse, new Cow);
trace(arr);
arr.splice(1, 1);
trace(arr);
trace(arr[0] , arr[1], arr[2]);
}
}
}
class Cat { }
class Dog { }
class Mouse { }
class Cow {}