1
let guideArray = ["TRUE", "TRUE", "TRUE", "FALSE", "TRUE", "TRUE", "FALSE"]
let toBeRemoved = ["ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN"]

I want to remove value which pairs FALSE according to index.

After operation output should be like this:

toBeRemoved = ["ONE", "TWO", "THREE", "FIVE", "SIX"]
//Forth and seventh element deleted

I tried to use for-in loop as shown below, but it failed because of every time it runs array count changes.

for i in 0...(guideArray.count-1) {
  if guideArray[i] == "FALSE"{
    toBeRemoved.removeAtIndex(i) 
    //this failed because of every time it runs toBeRemoved.count integer changes
  }
}
2
  • you can loop over the array backwards instead Commented Jul 22, 2016 at 14:56
  • What do you mean by looping over backwards? Commented Jul 22, 2016 at 14:57

3 Answers 3

4

Solution

This is a problem where Function Programming is a great help.

Given these 2 arrays

let words = ["ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN"]
let shouldRemove = ["TRUE", "TRUE", "TRUE", "FALSE", "TRUE", "TRUE", "FALSE"]

You can perform filter this way

let filtered = zip(words, shouldRemove).filter { $0.1 == "TRUE" }.map { $0.0 }
// ["ONE", "TWO", "THREE", "FIVE", "SIX"]

How does it work?

zip(words, shouldRemove)

This create a sequence where the n-th element of the first array is paired with the n-th element of the second array. So each element of this sequence has 2 components (one from words and one from shouldRemove).

zip(words, shouldRemove)
---------| ---------
ONE      | TRUE
---------| ---------
TWO      | TRUE
---------| ---------
THREE    | TRUE
---------| ---------
FOUR     | FALSE
---------| ---------
FIVE     | TRUE
---------| ---------
SIX      | TRUE
---------| ---------
SEVEN    | FALSE
---------| ---------

Next

.filter { $0.1 == "TRUE" }

This filter the sequence leaving only the elements where the second component is the string "TRUE"

---------| ---------
ONE      | TRUE
---------| ---------
TWO      | TRUE
---------| ---------
THREE    | TRUE
---------| ---------
FIVE     | TRUE
---------| ---------
SIX      | TRUE
---------| ---------

Next

.map { $0.0 }

Finally for each element, only the first component is returned (the one from words).

---------
ONE      
---------
TWO
---------
THREE
---------
FIVE 
---------
SIX 
---------

Why did I rename the names of your arrays?

  • In Swift we don't use the type of a variable into it's name. The name of a variable should represent it's value.
  • Names of your arrays are inverted. You named toBeRemoved the array with the content. Shouldn't it be the name with the "TRUE"/"FALSE"?
Sign up to request clarification or add additional context in comments.

Comments

3

Instead of starting at the first index and counting to the last index, you can start at the last index and count to the first index. If you remove an element from an array, it only shifts stuff after it, so by counting down, you make sure that the elements you still need to affect have not been moved around by removing previous elements.

In your example, you are trying to start at index 0 and count up to index 6. This means when you get to the fourth element in the array, and remove it, the remaining elements that you need to iterate over shift forwards one space, which is causing your issue.

In your case, this code will start at index 6, the last index, and see if that should be removed. It should be removed, so you will remove it. Then you will go to index 5, which is still "SIX", since removing the "SEVEN" from the end did not change the order of the elements before it.

for i in (guideArray.count - 1).stride(through: 0, by: -1) {
    if guideArray[i] == "FALSE"{
        toBeRemoved.removeAtIndex(i)
    }
}

Comments

3

Another solution, without zip/cycle:

let guideArray = ["TRUE", "TRUE", "TRUE", "FALSE", "TRUE", "TRUE", "FALSE"]
let toBeRemoved = ["ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN"]

let resultArray = guideArray.enumerate().flatMap{$0.element == "TRUE" ? toBeRemoved[$0.index] : nil}

print(resultArray)
// ["ONE", "TWO", "THREE", "FIVE", "SIX"]

Here we are flatMapping toBeRemoved array by replacing elements with nil in "FALSE" positions

2 Comments

No need to map then flatMap, just do a single flatMap ;)
@Hamish sure, what a funny thing I made! I will change answer with your suggest

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.