(I'm new to coding and to Swift; apologies in advance if the below is obvious to the more experienced. I've searched Stack Overflow and many other sites and have found only vaguely similar questions, but none quite the same as mine, and none with obviously adaptable answers. Here goes...)
My question is: In Swift, how can I evaluate an array of arrays and return a new array that has had a specified integer removed from each place it previously appeared? Without knowing if it is the best method to do this, I think what I want to do is filter a two dimensional array.
Here's the background: Using Swift Playgrounds, I'm building a simple simulation of how ranked-choice voting works. In my code, this involves creating an array that contains other arrays. My outer array, which I've named "allBallots," is a collection of inner arrays that each contain one or more integers. Each integer represents a vote for a specific candidate.
For instance, the allBallots array might look like this: [[3, 2, 0, 1], [1, 2], [4, 3, 2], [0, 1, 4, 2], [4, 1], [3], [3, 2, 0], [2], [0, 1], [4, 3, 1, 0], [1, 0, 2, 4]]. That represents eleven ballots (i.e., eleven inner arrays) - the first ballot ranks candidate 3 first, candidate 2 second, candidate 0 third, and candidate 1 last. The second ballot ranks candidate 1 first, ranks candidate 2 second, and ranks no one else, and so on for the other ballots.
After each simulated round of voting, my code combs the inner arrays (i.e., each "ballot") and tallies the number of "first-place votes" - that is, the values that appear first in each inner array. What it needs to do next is to remove all votes for the candidate who receives the fewest first-place votes. In the above example, 0 and 1 both appear first twice, 2 appears first once, and 3 and 4 each appear first three times. My code figures out which value appeared first the fewest times - in the above example, it's 2, since 2 appeared first just once. Now it's time to remove all the 2s wherever they appear, but this is the point at which I get stuck.
I want to remove all values of 2 from the whole "allBallots" array - not just the instances that appear first in inner arrays. I've tried .filter, but I can't figure out the syntax that would allow me to use it on a multidimensional array.
For instance, if I try...
var newBallots = allBallots.filter { $0 != 2 }
...I get an error reading Cannot convert value of type '[Int]' to expected argument type 'Int'. I gather that that one is a problem involving the type system - there's a mismatch between the types of what I specified (the integer 2) and the actual contents of my array (other arrays), but I've no idea how to fix it.
If instead I try...
var newBallots = allBallots.filter { $0 != [2] }
...I don't encounter an error, and something has been filtered, but only the inner array that consisted exactly of [2] is removed, rather than removing 2 from all locations throughout the array.
If instead I try...
var newBallots = allBallots.filter { $0.contains(2) }
...no errors, but newBallots contains only [[3, 2, 0, 1], [1, 2], [4, 3, 2], [0, 1, 4, 2], [3, 2, 0], [2], [1, 0, 2, 4]], which is odd. Four arrays have been removed/filtered, but I've no idea why those four. What I wanted was to remove each instance of "2," which is definitely not what happened. I imagine that I'm misusing ".contains" in this context, but I'm at the limits of my understanding here.
Can anyone suggest how I might go about removing a specific value from a two-dimensional array at each place that it exists? Any help much appreciated.
CodePudding user response:
Many ways to do this but you can use reduce or map
let allBallots = [[1, 2, 3], [1, 2]]
let newBallots = allBallots.reduce(into: []) { partial, element in
partial.append(element.filter { $0 != 2 })
}
print(newBallots) // [[1, 3], [1]]
let allBallots = [[1, 2, 3], [1, 2]]
let newBallots = allBallots.map { $0.filter { $0 != 2 } }
print(newBallots) // [[1, 3], [1]]
CodePudding user response:
If you are new to coding, you shouldn't use higher-order functions such as filter, map, reduce,... . You should code according to the flow of your thoughts.
// 2-dimensional
let allBallots = [[1, 2, 3], [1, 2], [2, 0, 3, 4]]
var newBallots = [[Int]]()
// travel each 1-dimensional in 2-dimensional
for ballot in allBallots {
//This code below equal: `let ballotRemoved2 = ballot.filter { $0 != 2 }`
var ballotRemoved2 = [Int]()
for element in ballot {
if element != 2 { ballotRemoved2.append(element) }
}
newBallots.append(ballotRemoved2)
}
print(newBallots)