Array of strings and ints in swift - arrays

I am trying to make an array of arrays where each nested array has a string and an integer.
I have seen you can use structs but all I want to do is make them a constant, and I want to know if there is a way to do it without having to type loads of extra stuff
let items: [[String, Int]] = [["A", 1], ["B", 2], ["C", 3]]

I think what you want is an array of tuples, not an array of arrays. That implementation would look like this:
let items: [(String, Int)] = [("A", 1), ("B", 2), ("C", 3)]
You could access these properties like this:
let itemOneString = items[0].0 // "A"
let itemOneInt = items[0].1 // 1

It will work for you:
let items: [[(String, Int)]] = [[("A", 1)], [("B", 2)], [("C", 3)]]

Array is collection of similar data types. It can not contain heterogeneous types data.
But if you still want to do it. There are other workarounds like create array of dictionary like this.
let items: [[String: Any]] = [["String" : "A", "Int" : 1], ["String" : "B", "Int" : 2]]
or create an array of Tuples.
let items: [(String, Int)] = [("A", 1), ("B", 2), ("C", 3)]
You can add any number of items in Tuple or Dictionary.

Related

Sort 2 linked arrays in Swift? [duplicate]

This question already has answers here:
In Swift how can I sort one array based on another array?
(4 answers)
Closed 4 years ago.
I want to sort one array and "mirror" order changes to another array of the same dimension.
The question is similar to this one but for Swift language:
Better way to sort 2 "linked" arrays?
Example:
let arr1 = ["a", "b", "c", "d", "e"]
let arr2 = [1, 5, 9, 2, 3]
... //sort
//result == ["a", "d", "e", "b", c"]
Approximate solution I know:
for i in stride(from 0, to: arr2.count - 1, by: 1) {
for j in stride(from i + 1, to: arr2.count, by: 1) {
if arr2[i] > arr2[j] {
...//swap arr2[i] and arr2[j]
...//swap arr1[i] and arr1[j]
}
}
}
But they added a lot of additional possibilities for advanced working with arrays in Swift. So is it possible to simplify this solution with inner Swift features?
Note: arr1 and arr2 are given as separate arrays.
EDITED
Yes. You found the similar questions but their titles are awful and don't reflect the answers their authors need. In other words if you delete/close my question the people may continue to ask it again because it is impossible to find something with existing titles!
zip the arrays together
sort the new array by array 2
map the array to array 1
let arr1 = ["a", "b", "c", "d", "e"]
let arr2 = [1, 5, 9, 2, 3]
let result = zip(arr1, arr2) // [("a", 1), ("b", 5), ("c", 9), ("d", 2), ("e", 3)]
.sorted(by: {$0.1 < $1.1}) // [("a", 1), ("d", 2), ("e", 3), ("b", 5), ("c", 9)]
.map{ $0.0 } // ["a", "d", "e", "b", "c"]

Map Swift array with specific format

Given an array that contains elements like this:
let array = [[a], [b, c], [d, e, f]]
Is there an elegant way to convert this array to an array which returns a tuple with the index of the outer array:
let result = [(a, 0), (b, 1), (c, 1), (d, 2), (e, 2), (f, 2)]
let array = [["a"], ["b", "c"], ["d", "e", "f"]]
let result = zip(array, array.indices).flatMap { subarray, index in
subarray.map { ($0, index) }
}
result is:
[("a", 0), ("b", 1), ("c", 1), ("d", 2), ("e", 2), ("f", 2)]
I used zip(array, array.indices) instead of array.enumerated() because you specifically asked for a tuple with the array index — enumerated() produces tuples with zero-based integer offsets. If your source collection is an array, it doesn't make a difference, but other collections (like ArraySlice) would behave differently.

Sort an array of tuples in swift 3

Hi I have the following
class MyClass {
var myString: String?
}
var myClassList = [String: MyClass]()
I would like to sort this array alphabetically by the myString variable in Swift 3 any pointers?
As mentioned above, you have a dictionary, not tuples.
However, Dictionaries do indeed have a sorted(by:) method that you can use to sort an array of Key/Value pair tuples. Here's an example:
var m: [String: Int] = ["a": 1]
let n = m.sorted(by: { (first: (key: String, value: Int), second: (key: String, value: Int)) -> Bool in
return first.value > second.value
})
That's expanded to show the full signature of the closure, however easily shorthanded to:
let n = m.sorted(by: {
return $0.value > $1.value
})
Additionally, you can also perform other enumerations over Dictionaries
m.forEach { (element: (key: String, value: Int)) in
print($0.value)
}
All of this is due to the Collection and sequence protocol hierarchies in Swift, they're some pretty nice abstractions.
Cool problem! Though i'd like to point out first that [String: MyClass] is a Dictionary and not at Tupule.
Swift does, however, support Tupules. The syntax for your tupule would look like so:
var tupule: (String, MyClass) = (foo, bar)
You would then need to make an Array of them:
var tupules:[(String, MyClass)] = [(foo, bar), (up, dog)]
Then you could sort that array:
tupules.sort({ $0[1].myString > $1[1].myString })
though you should probably define a more robust sort mechanism.
These are the contents of the sort closure:
$0 is the one of the objects for which needs to be compared, $1 is the other.
$0[1] and $1[1] accesses the objects' 1 index, in this case, as defined in your tupule, it is your custom object MyClass
Hope this helps.
Swift version 5.2.4
let arr = [(0, 5), (1, 2), (2, 4), (3, 1), (4, 3)] // -> [(3, 1), (1, 2), (4, 3), (2, 4), (0, 5)]
let sorted = arr.sorted{ $0.1 < $1.1 }

In Swift how can I sort one array based on another array?

In Swift, say I have two arrays:
var array1: [Double] = [1.2, 2.4, 20.0, 10.9, 1.5]
var array2: [Int] = [1, 0, 2, 0, 3]
Now, I want to sort array1 in ascending order and reindex array2 accordingly so that I get
array1 = [1.2, 1.5, 2.4, 10.9, 20.4]
array2 = [1, 3, 0, 0, 2]
Is there a simple way to do this using Swift functions or syntax?
I know I can build a function to do it and can keep track of indices, but I'm curious if there is a more elegant solution.
let array1: [Double] = [1.2, 2.4, 20.0, 10.9, 1.5]
let array2: [Int] = [1, 0, 2, 0, 3]
// use zip to combine the two arrays and sort that based on the first
let combined = zip(array1, array2).sorted {$0.0 < $1.0}
print(combined) // "[(1.2, 1), (1.5, 3), (2.4, 0), (10.9, 0), (20.0, 2)]"
// use map to extract the individual arrays
let sorted1 = combined.map {$0.0}
let sorted2 = combined.map {$0.1}
print(sorted1) // "[1.2, 1.5, 2.4, 10.9, 20.0]"
print(sorted2) // "[1, 3, 0, 0, 2]"
Sorting more than 2 arrays together
If you have 3 or more arrays to sort together, you can sort one of the arrays along with its offsets, use map to extract the offsets, and then use map to order the other arrays:
let english = ["three", "five", "four", "one", "two"]
let ints = [3, 5, 4, 1, 2]
let doubles = [3.0, 5.0, 4.0, 1.0, 2.0]
let roman = ["III", "V", "IV", "I", "II"]
// Sort english array in alphabetical order along with its offsets
// and then extract the offsets using map
let offsets = english.enumerated().sorted { $0.element < $1.element }.map { $0.offset }
// Use map on the array of ordered offsets to order the other arrays
let sorted_english = offsets.map { english[$0] }
let sorted_ints = offsets.map { ints[$0] }
let sorted_doubles = offsets.map { doubles[$0] }
let sorted_roman = offsets.map { roman[$0] }
print(sorted_english)
print(sorted_ints)
print(sorted_doubles)
print(sorted_roman)
Output:
["five", "four", "one", "three", "two"]
[5, 4, 1, 3, 2]
[5.0, 4.0, 1.0, 3.0, 2.0]
["V", "IV", "I", "III", "II"]
You could "link" the items of each array by mapping over the indices to create an array of tuples, then sort the tuples according to the first array's values before extracting the original arrays.
assert(array1.count == array2.count, "The following technique will only work if the arrays are the same length.")
let count = array1.count
// Create the array of tuples and sort according to the
// first tuple value (i.e. the first array)
let sortedTuples = (0..<count).map { (array1[$0], array2[$0]) }.sort { $0.0 < $1.0 }
// Map over the sorted tuples array to separate out the
// original (now sorted) arrays.
let sortedArray1 = sortedTuples.map { $0.0 }
let sortedArray2 = sortedTuples.map { $0.1 }
Swift 4
This part is translated from #vacawama's answer to Swift 4 syntax
let array1: [Double] = [1.2, 2.4, 20.0, 10.9, 1.5]
let array2: [Int] = [1, 0, 2, 0, 3]
// use zip to combine the two arrays and sort that based on the first
let combined = zip(array1, array2).sorted(by: {$0.0 < $1.0})
print(combined) // "[(1.2, 1), (1.5, 3), (2.4, 0), (10.9, 0), (20.0, 2)]"
// use map to extract the individual arrays
let sorted1 = combined.map {$0.0}
let sorted2 = combined.map {$0.1}
print(sorted1) // "[1.2, 1.5, 2.4, 10.9, 20.0]"
print(sorted2) // "[1, 3, 0, 0, 2]"
The above logic can be expanded for three or more arrays:
(slow)
let array1: [Double] = [1.2, 2.4, 20.0, 10.9, 1.5]
let array2: [Int] = [1, 0, 2, 0, 3]
let array3: [Float] = [3.3, 1.1, 2.5, 5.1, 9.0]
// use zip to combine each (first, n.th) array pair and sort that based on the first
let combined12 = zip(array1, array2).sorted(by: {$0.0 < $1.0})
let combined13 = zip(array1, array3).sorted(by: {$0.0 < $1.0})
// use map to extract the individual arrays
let sorted1 = combined12.map {$0.0}
let sorted2 = combined12.map {$0.1}
let sorted3 = combined13.map {$0.1}
As #Duncan C pointed out, this approach is not very efficient as the first array is sorted repeatedly. #vacawama's approach should be used instead, which in Swift 4 syntax is:
(fast)
let offsets = array1.enumerated()sorted(by: {$0.element < $1.element}).map {$0.offset}
let sorted1 = offsets.map {array1[$0]}
let sorted2 = offsets.map {array2[$0]}
let sorted3 = offsets.map {array3[$0]}
Though not especially elegant, a simple solution when working with arrays of objects which must be compared, and whose orders are not known and may not even share the same lengths, is to loop the "ordered" array, finding the matching object in the unordered array, and appending it to a new empty array:
var sorted: [Foo] = []
// Loop the collection whose order will define the other
for item in originalOrder {
// Find the item in the unsorted collection
if let next = unsortedItems.first(where: { $0 === item }) {
// Move the item to the new collection, thus achieving order parity
sorted.append(next)
}
}
This is useful when you have an operation that provides a transformed version of a collection which may have 0..<original.count number of items in any order, and you want to get back to the original order using pointer/object equality.
If you need to additionally maintain index parity, you can skip the if let and just append the result of first(where:) directly into sorted, which will put nil into the blanks.
Note that this example solution will additionally act as a filter for items which are either duplicated or not in the original, which may or may not be what you want. Modify to your needs.

F# filter a two-dimensional array with multiple arguments

I've been stuck for a while with this seemingly basic problem. I have a two dimensional array of strings and another one dimensional array of strings. The one dimensional array consists of some of the elements present in one of the columns of the two dimensional array. The result that I wish to get is a two dimensional array which is filtered by the elements in the two-dimensional array. As an example:
two-dimensional array:
[["A", "elephant"], ["B", "dog"] , ["C", "cat"] , ["D", "mouse"], ["E", "giraffe"]]
one-dimensional array:
["elephant" , "cat" , "giraffe"]
desired result:
[["A", "elephant] , ["C", "cat"] , ["E", "giraffe"]]
I thank you in advance for your help. I'm pretty new to F# and trying to learn it has been difficult until now.
cheers
Let's say you have a list of tuples like this one:
let animalList =
[("A", "elephant"); ("B", "dog"); ("C", "cat"); ("D", "mouse"); ("E", "giraffe")]
And another list of animals you want to keep, let's make it a set while we're at it:
let animalsToKeep =
["elephant"; "cat"; "giraffe"] |> Set.ofList
Then define a function that filters a list of tuples, keeping only those that appear in a given set
let filterWithSet set lst =
lst
|> List.filter (fun (_, elem) -> Set.contains elem set)
And call it:
filterWithSet animalsToKeep animalList
The answer depends on what you actually want to do, but it sounds like finding the right representation is the most important part of the question. In your example, your nested lists always contain just two values (e.g. "A" and "elephant") and so it would make more sense to use a list of tuples:
let things = [ ("A", "elephant"); ("B", "dog"); ("C", "cat");
("D", "mouse"); ("E", "giraffe")]
This representation will make things easier, because we only need to check if the second element of the tuple is in the list used for filtering:
let filter = ["elephant" ; "cat" ; "giraffe"]
To do that, you can use List.filter to filter a list. In the condition, you can get the animal using snd (get second element of a tuple) and then use List.exist to see if it is in the list of animals to be included:
things |> List.filter (fun nested ->
let animal = snd nested
filter |> List.exists (fun a -> a = animal))
If you want to make the lookup more efficient, you can create a set of filtered items:
let filter = set ["elephant" ; "cat" ; "giraffe"]
things |> Seq.filter (fun nested -> filter.Contains(snd nested))
And, in fact, you can use function composition to call snd followed by the check:
things |> Seq.filter (snd >> filter.Contains)
This means exactly the same thing as the line above - it takes the tuple with the letter and animal name, extracts the animal name using the snd function and then passes the name to filter.Contains to see if it is in the set.

Resources