I'm using Swift 2.3 and I have the following type array of arrays of my custom object called Player
`var playing = [[obj-one, obj-two],[obj-three, obj-four]]`
How would I use a for-in loop or something else so I can get the array index and the object?
I have the following:
for (index, p) in playing { -- Expression type [[Player]] is ambigious
I've also tried
for in (index, p: Player) in playing { -- same result.
and
for in (index, p) in playing as! Player { -- doesn't conform to squence type
I want to just be able to print out which array the object belongs to and then work with that current object
Use enumerated() to pair up an index and an element, like this:
let a = [["hello", "world"], ["quick", "brown", "fox"]]
for outer in a.enumerated() {
for inner in outer.element.enumerated() {
print("array[\(outer.offset)][\(inner.offset)] = \(inner.element)")
}
}
This produces the following output:
array[0][0] = hello
array[0][1] = world
array[1][0] = quick
array[1][1] = brown
array[1][2] = fox
Functional approach:
let items = [["0, 0", "0, 1"], ["1, 0", "1, 1", "1, 2"]]
items.enumerated().forEach { (firstDimIndex, firstDimItem) in
firstDimItem.enumerated().forEach({ (secondDimIndex, secondDimItem) in
print("item: \(secondDimItem), is At Index: [\(firstDimIndex), \(secondDimIndex)]")
})
}
prints:
item: 0, 0, is At Index: [0, 0]
item: 0, 1, is At Index: [0, 1]
item: 1, 0, is At Index: [1, 0]
item: 1, 1, is At Index: [1, 1]
item: 1, 2, is At Index: [1, 2]
I wouldn't use a for loop, I would do something like this:
import Foundation
var playing = [["one", "two"], ["three", "four"]]
if let index = playing.index(where: { $0.contains("two") }) {
print(index)
} else {
print("Not found")
}
This prints:
0
Or to get the entire subarray containing what you want:
if let subarray = playing.first(where: { $0.contains("three") }) {
print(subarray)
} else {
print("Not found")
}
Prints:
["three", "four"]
Related
I have simple two dimensional array with different number of elements in each row. My goal is find next element with elegant solution.
If we have the last element we should get the first one.
[
[1, 0, 234, 345, 678],
[123, 456, 789],
[0, 9]
]
678 -> 123
9 -> 1
If mat is a nested array then mat.joined() is a “lazy flat collection” of all the nested elements. That suggest to solve the problem for arbitrary collections:
Given a collection and an element of that collection, return the immediate successor of the element. The collection is viewed as cyclic, so that the successor of the last element is the first element.
A straight-forward implementation is (explanations inline):
extension Collection where Element: Comparable {
func cyclicNext(after elem: Element) -> Element? {
// Find (first) index of given element
guard var idx = firstIndex(of: elem) else {
return nil // Element not present in collection
}
formIndex(after: &idx) // Advance index by one
if idx == endIndex { // If past the end ...
idx = startIndex // ... then jump back to the start
}
return self[idx]
}
}
If the element occurs more than once in the collection then the successor of the first occurrence is returned.
This can be applied to your matrix:
let mat: [[Int]] = [[1, 0, 234, 345, 678],
[123, 456, 789],
[0, 9]]
print(mat.joined().cyclicNext(after: 1)) // 0
print(mat.joined().cyclicNext(after: 678)) // 123
print(mat.joined().cyclicNext(after: 123)) // 456
print(mat.joined().cyclicNext(after: 9)) // 1
print(mat.joined().cyclicNext(after: 999)) // nil
It can also be applied to other collections:
print([1, 3, 5, 7].cyclicNext(after: 3)) // 5
print([1, 3, 5, 7].cyclicNext(after: 7)) // 1
print("Hello World".cyclicNext(after: "W")) // "o"
print("Hello World".cyclicNext(after: "d")) // "H"
I believe this does do the job :
extension Array where Element == [Int] {
func element(after x: Int) -> Int? {
var arrayIndex = 0
while arrayIndex < self.count {
//If an array contains the searched element
if let xIndex = self[arrayIndex].firstIndex(where: { $0 == x }) {
//if the next element is in the same array
if xIndex < self[arrayIndex].count - 1 {
return self[arrayIndex][xIndex + 1]
}
//if the next element is in the next array
else if arrayIndex < self.count - 1 {
return self[arrayIndex + 1][0]
}
//if the x is the last element in the last array
else {
return self[0][0]
}
}
arrayIndex += 1
}
return nil
}
}
And here are some test cases :
let mat = [
[1, 0, 234, 345, 678],
[123, 456, 789],
[0, 9]
]
mat.element(after: 678) //123
mat.element(after: 9) //1
mat.element(after: 1) //0
mat.element(after: 0) //234
mat.element(after: 3) //nil
A possible solution is to flatten your array.
Tested on Playground:
let mat: [[Int]] = [[1, 0, 234, 345, 678],
[123, 456, 789],
[0, 9]]
func next(element: Int, in matrix: [[Int]]) -> Int? {
let flatten = matrix.flatMap({ $0 })
guard let index = flatten.firstIndex(of: element) else {
print("Element \(element) not found in matrix")
return nil
}
if index == flatten.endIndex - 1 {
return flatten.first
} else {
return flatten[index + 1 ]
}
}
let result1 = next(element: 678, in: mat)
print("End of subarray Test: \(result1)")
let result2 = next(element: 9, in: mat)
print("Last one Test: \(result2)")
let result3 = next(element: 66, in: mat)
print("Not present test: \(result3)")
let result0 = next(element: 1, in: mat)
print("First one test: \(result0)")
Output:
$>End of subarray Test: Optional(123)
$>Last one Test: Optional(1)
$>Element 66 not found in matrix
$>Not present test: nil
$>First one test: Optional(0)
I don't know if it's enough elegant for you.
One optimization would have to keep the flatten and not recalculate each time.
I'm looking to do a "reverse zip" on a collection. Essentially meaning:
let a = [1, 2, 3, 4, 5, 6, 7]
let (left, right) = a.reverseZip()
print(left) // [1, 3, 5, 7]
print(right) // [2, 4, 6]
I have an implementation that seems to work (see my answer listed below), but I have two questions that I'm hoping we could discuss:
What's the correct name for this kind of operation? It's not exactly the opposite of zip (and I'm certain this pattern exists elsewhere in another functional language)
Is there a more optimal algorithm for this method?
This is a seed of an idea. Instead of returning arrays, it would be more efficient to return sequences.
Here is an Array extension that returns two LazyMapSequence<StrideTo<Int>, Element>. The advantage is that is doesn't actually generate the sequences; they are provided on demand because they're lazy. This is much like the way reversed() works on an Array.
extension Array {
func reverseZip() -> (LazyMapSequence<StrideTo<Int>, Element>, LazyMapSequence<StrideTo<Int>, Element>) {
let left = stride(from: 0, to: self.count, by: 2).lazy.map { self[$0] }
let right = stride(from: 1, to: self.count, by: 2).lazy.map { self[$0] }
return (left, right)
}
}
Example:
let a = [1, 2, 3, 4, 5, 6, 7]
let (left, right) = a.reverseZip()
// iterate left
for i in left {
print(i)
}
1
3
5
7
// turn them into arrays to print them
print(Array(left))
[1, 3, 5, 7]
print(Array(right))
[2, 4, 6]
I'm sure this can be generalized to something more general than Array. That is left as an exercise to the reader.
If you want two arrays
If you really want the two arrays, then you'd just return the result of map (taking out lazy):
extension Array {
func reverseZip() -> ([Element], [Element]) {
let left = stride(from: 0, to: self.count, by: 2).map { self[$0] }
let right = stride(from: 1, to: self.count, by: 2).map { self[$0] }
return (left, right)
}
}
Based on vacawama's idea to return lazy collections instead of arrays,
here is a possible generalization to arbitrary collections with arbitrary strides:
extension Collection {
func makeStrideIterator(stride: Int) -> AnyIterator<Element> {
precondition(stride > 0, "The stride must be positive")
var it = makeIterator()
var first = true
return AnyIterator<Element> {
if first {
first = false
} else {
// Skip `stride - 1` elements:
for _ in 1..<stride {
guard let _ = it.next() else { return nil }
}
}
return it.next()
}
}
}
The method returns an AnyIterator (which is both an iterator and a sequence)
of the collection elements at the positions
0, stride, 2*stride, ...
Unzipping an array (or any other collection) can then be done with
let a = [1, 2, 3, 4, 5]
let left = a.makeStrideIterator(stride: 2)
let right = a.dropFirst().makeStrideIterator(stride: 2)
for e in left { print(e) }
// 1, 3, 5
for e in right { print(e) }
// 2, 4
Here is an example for picking every third character from a string:
for c in "ABCDEFG".makeStrideIterator(stride: 3) {
print(c)
}
// A D G
The special case of unzipping into two arrays can then be implemented as
extension Collection {
func unzip() -> ([Element], [Element]) {
return (Array(makeStrideIterator(stride: 2)),
Array(dropFirst().makeStrideIterator(stride: 2)))
}
}
Example:
print([1, 2, 3, 4, 5].unzip())
// ([1, 3, 5], [2, 4])
extension Collection {
func reverseZip() -> ([Element], [Element]) {
var left = [Element]()
var right = [Element]()
let capacity = Double(self.count) / 2.0
left .reserveCapacity(Int(ceil(capacity)))
right.reserveCapacity(self.count / 2)
for element in self {
if left.count > right.count {
right.append(element)
} else {
left.append(element)
}
}
return (left, right)
}
}
UPDATE (03/10/18)
Thank you all for your help. Seems like a lot of you latched onto Stride, which is definitely a good way to go. I resisted this approach for 2 reasons:
I'm keen to keep this function as an extension of Collection, rather than Array
Strictly speaking, the startIndex of a given Collection isn't necessary 0, and the progression of indexes isn't necessarily an increment of 1 each time.
Martin R's approach of using a custom iterator definitely seemed like the right approach, so I used that along with the WWDC 2018's implementation of "EveryOther" to create a standard sequential iterator that simply goes along two elements in each while loop block, till it hits the end.
extension Collection {
func deinterleave() -> ([Element], [Element]) {
let smallHalf = count / 2
let bigHalf = count - smallHalf
var left = [Element]()
var right = [Element]()
left .reserveCapacity(bigHalf)
right.reserveCapacity(smallHalf)
let start = self.startIndex
let end = self.endIndex
var iter = start
while iter != end {
left.append(self[iter])
iter = index(after: iter)
if iter == end { break }
right.append(self[iter])
iter = index(after: iter)
}
return (left, right)
}
}
let a = [1,2,3,4,5,6,7]
print(a.deinterleave()) // ([1, 3, 5, 7], [2, 4, 6])
Without a doubt, there is some scope to improve this further and use vacawama's LazyMapSequence implementation, but for now this does everything I need it to.
So I am trying to iterate over an NSArray. My NSArray is an array of an array of strings. Here is a copy-paste of the first 1.5 elements
(
(
"Tater Tot Nachos",
"Fried Feta",
"The Ultimate Feta Bread",
"Cheese Bread",
"Aubrees Bread",
"The Wings!",
"Coconut Grove Chicken Sicks",
"Far East Wings",
"Bacon Brussels Sprouts"
),
(
"Shaved Brussels Sprout Salad",
"Greek Salad",
"Coronado Cobb Salad",
"Harvest Salad",
This is the function that's giving me the headache
func createMenu() {
if let list = cellDescripters {
for(index, item) in list.enumerated() {
for food in item {
//DO SOMETHING WITH "FOOD"
}
}
}
}
' cellDescripters ' Is a global variable and it is the array I was outlining at the top, basically an array of arrays of strings.
When I print the type of ' item ' I see it's of type __NSArrayM which is an NSMutableArray from my understanding. Looking at documentation NSMutableArrays are iterable.
However when I go to compile this code I get the error:
Type 'Any' does not conform to protocol 'Sequence'
Any help would be greatly appreciated.
I think following example give you help
for example i have array of string array like you =
[["beverages", "food", "suppliers"],["other stuff", "medicine"]];
var arrayExample = [["beverages", "food", "suppliers"],["other stuff", "medicine"]];
//Iterate array through for loop
for(index, item) in arrayExample.enumerated()
{
for food in item
{
print("index : \(index) item: \(food)")
}
}
OUTPUT
index : 0 item: beverages
index : 0 item: food
index : 0 item: suppliers
index : 1 item: other stuff
index : 1 item: medicine
Here's a more generic solution for iterating 2D arrays in Swift. Tested in Swift 4 on iOS 13.
Works only on Swift arrays, see the following link for converting your NSArray to Arrays: https://stackoverflow.com/a/40646875/1960938
// 2D array extension explanation: https://stackoverflow.com/a/44201792/1960938
fileprivate extension Array where Element : Collection, Element.Index == Int {
typealias InnerCollection = Element
typealias InnerElement = InnerCollection.Iterator.Element
func matrixIterator() -> AnyIterator<InnerElement> {
var outerIndex = self.startIndex
var innerIndex: Int?
return AnyIterator({
guard !self.isEmpty else { return nil }
var innerArray = self[outerIndex]
if !innerArray.isEmpty && innerIndex == nil {
innerIndex = innerArray.startIndex
}
// This loop makes sure to skip empty internal arrays
while innerArray.isEmpty || (innerIndex != nil && innerIndex! == innerArray.endIndex) {
outerIndex = self.index(after: outerIndex)
if outerIndex == self.endIndex { return nil }
innerArray = self[outerIndex]
innerIndex = innerArray.startIndex
}
let result = self[outerIndex][innerIndex!]
innerIndex = innerArray.index(after: innerIndex!)
return result
})
}
}
An example usage:
let sampleMatrix = [
["a", "b", "c"],
["d", "e"],
[],
["f"],
[]
]
// Should print: a, b, c, d, e, f
for element in sampleMatrix.matrixIterator() {
print(element)
}
I'm trying to implement a groupBy functionality where all the numbers of a nested list are grouped. My code so far:
struct MyClass {
var numbers: [Int]
...
}
var dict: [String : MyClass] = ...
let numbers = dict
.filter{ $0.0.containsString(searchString) }
.flatMap{ $0.1.numbers }
This yields me an Array of Ints. However I'd like to have a dictionary [Int : Int] with each unique number and the count of its occurence. So for example:
[1,2,3,4,1,2,2,1]
should be:
[1 : 2, 2 : 3, 3 : 1, 4 : 1]
I know there's a groupBy operator, but Swift doesn't seem to have one. I've tried with reduce:
func reducer(accumulator: [Int: Int], num: Int) -> [Int : Int] {
var acc = accumulator
acc[num]! += 1
return acc
}
filtered.reduce([:], combine: reducer)
But it crashes when I want to run it. Not sure why, I get a EXC_BAD_INSTRUCTION.
I'd appreciate any help.
let numbers = [1,2,3,4,1,2,2,1]
var results = [Int: Int]()
Set(numbers).forEach { number in results[number] = numbers.filter { $0 == number }.count }
print(results) // [2: 3, 3: 1, 1: 3, 4: 1]
Actually I'm not very sure if this is what you want. I just looked at your examples.
Using NSCountedSet:
var objects = [1,2,3,4,1,2,2,1]
let uniques = NSCountedSet(array: objects)
uniques.forEach { results[$0 as! Int] = uniques.countForObject($0) }
print(results) // [2: 3, 3: 1, 1: 3, 4: 1]
I would expect the crash to be ocurring on this line:
acc[num]! += 1
The first time this is called for a number, the entry doesn't exist in the dictionary yet so acc[num] is nil. Forcefully unwrapping it would cause a crash.
Not sure if this is the best solution but you can simple check for this case:
if (acc[num]) {
acc[num]! += 1
} else {
acc[num] = 1
}
Cleaner code from #vacawama in the comments:
acc[num] = (acc[num] ?? 0) + 1
Here's an extension to Array that does what you're asking:
extension Array where Element: Hashable {
var grouped: [Element:Int] {
var dict = [Element:Int]()
self.forEach { dict[$0] = (dict[$0] ?? 0) + 1 }
return dict
}
}
The key is the closure: { dict[$0] = (dict[$0] ?? 0) + 1 }.
It takes the current value in the array, tests to see if it's a key in the dictionary, returns the value for that key if it exists or 0 if it doesn't, then adds one and sets the key:value to be the pair of the current value and occurrences so far.
Example use:
[1,2,3,4,1,2,2,1].grouped // => [2: 3, 3: 1, 1: 3, 4: 1]
You need something like this:
if let _ = acc.indexForKey(num) {
acc[num]! += 1
}
else {
acc[num] = 1
}
It's sort of unclear what you're asking for, but here's a function that will take an array of ints and return a dictionary with the number as the key, and the count as the value:
func getDictionaryOfCounts(accumulator: [Int]) -> [Int : Int] {
var countingDictionary: [Int : Int] = [:]
accumulator.forEach { (value) in
if countingDictionary[value] != nil {
countingDictionary[value]! += 1
}
else{
countingDictionary[value] = 1
}
}
return countingDictionary
}
1) this is first step : I'm asking how to make this dictionary in order by value.
2)second step: I want to split this dictionary in two array, one for value and one for keys thanks
["fruit": 1, "vegie": 13, "money": 46, "Canada": 219, "cash": 1, "lola": 1, "tv": 2, "bed": 1, "sofa": 1]
I did something like that but I want to split in two arrays now
let byValue = {
(elem1:(key: String, val: Int), elem2:(key: String, val: Int))->Bool in
if elem1.val < elem2.val {
return true
} else {
return false
}
}
let sortedDict = dict.sort(byValue)
Assuming that the part 1 of your question is done as you said in your last edit, here is how to have...
The keys:
let keys = sortedDict.map { $0.0 }
And the values:
let values = sortedDict.map { $0.1 }