So, say I have a dictionary that looks like this:
var data : [Float:Float] = [0:0,1:1,2:1.414,3:2.732,4:2,5:5.236,6:3.469,7:2.693,8:5.828,9:3.201]
How would I programmatically find the highest value in the dictionary? Is there a "data.max" command or something?
let maximum = data.reduce(0.0) { max($0, $1.1) }
Just a quick way using reduce.
or:
data.values.max()
Output:
print(maximum) // 5.828
A Swift Dictionary provides the max(by:) method. The Example from Apple is as follows:
let hues = ["Heliotrope": 296, "Coral": 16, "Aquamarine": 156]
let greatestHue = hues.max { a, b in a.value < b.value }
print(greatestHue)
// Prints "Optional(("Heliotrope", 296))"
Exist a function in the API, named maxElement you can use it very easy , that returns the maximum element in self or nil if the sequence is empty and that requires a strict weak ordering as closure in your case as you use a Dictionary. You can use like in the following example:
var data : [Float:Float] = [0:0,1:1,2:1.414,3:2.732,4:2,5:5.236,6:3.469,7:2.693,8:5.828,9:3.201]
let element = data.maxElement { $0.1 < $1.1} // (.0 8, .1 5.828)
And get the maximum value by the values, but you can change as you like to use it over the keys, it's up to you.
I hope this help you.
Honestly the solutions mentioned above - work, but they seem to be somewhat unclear to me as a newbie, so here is my solution to finding the max value in a Dictionary using SWIFT 5.3 in Xcode 12.0.1:
var someDictionary = ["One": 41, "Two": 17, "Three": 23]
func maxValue() {
let maxValueOfSomeDictionary = someDictionary.max { a, b in a.value < b.value }
print(maxValueOfSomeDictionary!.value)
}
maxValue()
After the dot notation (meaning the ".") put max and the code inside {} (curly braces) to compare the components of your Dictionary.
There are two methods to find max value in the dictionary.
First approach:
data.values.max
Second approach:
data.max { $0.value < $1.value}?.value
If you want to find max key:
data.max { $0.key < $1.key}?.key
Related
This question already has answers here:
What is the reduce() function doing, in Swift
(4 answers)
Closed 9 months ago.
Here is a piece of code I don't understand. This code uses swift's reduce(::) function along with the closure which I am having trouble to understand. What are the values set in maxVerticalPipCount and maxHorizontalPipCount? Are they 5 and 2 respectively?
let pipsPerRowForRank = [[0], [1], [1,1], [1,1,1], [2,2], [2,1,2],
[2,2,2], [2,1,2,2], [2,2,2,2], [2,2,1,2,2],
[2,2,2,2,2]]
let maxVerticalPipCount = CGFloat(pipsPerRowForRank.reduce(0) { max($1.count, $0) })
let maxHorizontalPipCount = CGFloat(pipsPerRowForRank.reduce(0) { max($1.max() ?? 0, $0) })
By the way, if you’re wondering what precisely reduce does, you can always refer to the source code, where you can see the actual code as well as a nice narrative description in the comments.
But the root of your question is that this code is not entirely obvious. I might suggest that if you’re finding it hard to reason about the code snippet, you can replace the opaque shorthand argument names, $0 and $1, with meaningful names, e.g.:
let verticalMax = pipsPerRowForRank.reduce(0) { previousMax, nextArray in
max(nextArray.count, previousMax)
}
let horizontalMax = pipsPerRowForRank.reduce(0) { previousMax, nextArray in
max(nextArray.max() ?? 0, previousMax)
}
By using argument names that make the functional intent more clear, it often is easier to grok what the code is doing. IMHO, especially when there are multiple arguments, using explicit argument names can make it more clear.
That having been said, I’d probably not use reduce and instead do something like:
let verticalMax = pipsPerRowForRank
.lazy
.map { $0.count }
.max() ?? 0
To my eye, that makes the intent extremely clear, namely that we’re counting how many items are in each sub-array and returning the maximum count.
Likewise, for the horizontal one:
let horizontalMax = pipsPerRowForRank
.lazy
.flatMap { $0 }
.max() ?? 0
Again, I think that’s clear that we’re creating a flat array of the values, and then getting the maximum value.
And, in both cases, we’re using lazy to avoid building interim structures (in case our arrays were very large), but evaluating it as we go along. This improves memory characteristics of the routine and the resulting code is more efficient. Frankly, with an array of arrays this small, lazy isn’t needed, but I include it for your reference.
Bottom line, the goal with functional patterns is not to write code with the fewest keystrokes possible (as there are more concise renditions we could have written), but rather to write efficient code whose intent is as clear as possible with the least amount of cruft. But we should always be able to glance at the code and reason about it quickly. Sometimes if further optimization is needed, we’ll make a conscious decision to sacrifice readability for performance reasons, but that’s not needed here.
This is what the reduce functions do here
var maxVerticalPipCount:CGFloat = 0
for rark in pipsPerRowForRank {
if CGFloat(rark.count) > maxVerticalPipCount {
maxVerticalPipCount = CGFloat(rark.count)
}
}
var maxHorizontalPipCount:CGFloat = 0
for rark in pipsPerRowForRank {
if CGFloat(rark.max() ?? 0) > maxHorizontalPipCount {
maxHorizontalPipCount = CGFloat(rark.max() ?? 0)
}
}
You shouldn't use reduce(::) function for finding the max value. Use max(by:)
function like this
let maxVerticalPipCount = CGFloat(pipsPerRowForRank.max { $0.count < $1.count }?.count ?? 0)
let maxHorizontalPipCount = CGFloat(pipsPerRowForRank.max { ($0.max() ?? 0) < ($1.max() ?? 0) }?.max() ?? 0)
The reduce function loops over every item in a collection, and combines them into one value. Think of it as literally reducing multiple values to one value. [Source]
From Apple Docs
let numbers = [1, 2, 3, 4]
let numberSum = numbers.reduce(0, { x, y in
x + y
})
// numberSum == 10
In your code,
maxVerticalPipCount is iterating through the whole array and finding the max between count of 2nd element and 1st element of each iteration.
maxHorizontalPipCount is finding max of 2nd element's max value and first element.
Try to print each element inside reduce function for better understandings.
let maxVerticalPipCount = pipsPerRowForRank.reduce(0) {
print($0)
return max($1.count, $0)
}
Reduce adds together all the numbers in an array opens a closure and really do whatever you tell it to return.
let pipsPerRowForRank = [[1,1], [2,2,2]]
let maxVerticalPipCount = CGFloat(pipsPerRowForRank.reduce(0) {
max($1.count, $0)})
Here it starts at 0 at reduce(0) and loops through the full array. where it takes the highest value between it's previous value it's in process of calculating and the number of items in the subarray. In the example above the process will be:
maxVerticalPipCount = max(2, 0)
maxVerticalPipCount = max(3, 2)
maxVerticalPipCount = 3
As for the second one
let pipsPerRowForRank = [[1,2], [1,2,3], [1,2,3,4], []]
let maxHorizontalPipCount = CGFloat(pipsPerRowForRank.reduce(0) {
max($1.max() ?? 0, $0)})
Here we instead of checking count of array we check the max value of the nested array, unless it's empty, the it's 0. So here goes this one:
let maxHorizontalPipCount = max(2, 0)
let maxHorizontalPipCount = max(3, 2)
let maxHorizontalPipCount = max(4, 3)
let maxHorizontalPipCount = max(0, 4)
let maxHorizontalPipCount = 4
Example With Swift 5,
enum Errors: Error {
case someError
}
let numbers = [1,2,3,4,5]
let inititalValue = 0
let sum = numbers.reduce(Result.success(inititalValue)) { (result, value) -> Result<Int, Error> in
if let initialValue = try? result.get() {
return .success(value + initialValue)
} else {
return .failure(Errors.someError)
}
}
switch sum {
case .success(let totalSum):
print(totalSum)
case .failure(let error):
print(error)
}
I've looked through the methods here but I can't quite find what I'm looking for. I'm new-ish to Swift. I would like to extract a subset from a Dictionary based on a Set of key values, preferably without a loop.
For example, if my key Set is of type Set<String> and I have a Dictionary of type Dictionary<String, CustomObject>, I would like to create a new Dictionary of type Dictionary<String, CustomObject> that contains only the key-value pairs associated with the keys in the Set of Strings.
I can see that I could do this with for loop, by initializing a new Dictionary<String, CustomObj>(), checking if the original Dictionary contains a value at each String in the set, and adding key-value pairs to the new Dictionary. I am wondering if there is a more efficient/elegant way to do this however.
I'd be open to finding the subset with an Array of Strings instead of a Set if there is a better way to do it with an Array of keys.
Many thanks!
Swift 5 - You can do this very simply:
let subsetDict = originalDict.filter({ mySet.contains($0.key)})
The result is a new dictionary with the same type as the original but which only contains the key-value pairs corresponding to the keys in mySet.
Your assumption is correct, there is a more concise/swift-ish way to accomplish what you need.
For example you can do it via reduce, a functional programming concept available in Swift:
let subDict = originalDict.reduce([String: CustomObject]()) {
guard mySet.contains($1.key) else { return $0 }
var d = $0
d[$1.key] = $1.value
return d
}
Or, in two steps, first filtering the valid elements, and then constructing back the dictionary with the filtered elements:
let filteredDict = originalDict.filter { mySet.contains($0.key) }
.reduce([CustomObject]()){ var d = $0; d[$1.key]=$1.value; return d }
forEach can also be used to construct the filtered dictionary:
var filteredDict = [CustomObject]()
mySet.forEach { filteredDict[$0] = originalDict[$0] }
, however the result would be good it it would be immutable:
let filteredDict: [String:CustomObject] = {
var result = [String:CustomObject]()
mySet.forEach { filteredDict2[$0] = originalDict[$0] }
return result
}()
Dummy type:
struct CustomObject {
let foo: Int
init(_ foo: Int) { self.foo = foo }
}
In case you'd like to mutate the original dictionary (instead of creating a new one) in an "intersect" manner, based on a given set of keys:
let keySet = Set(["foo", "baz"])
var dict = ["foo": CustomObject(1), "bar": CustomObject(2),
"baz": CustomObject(3), "bax": CustomObject(4)]
Set(dict.keys).subtracting(keySet).forEach { dict.removeValue(forKey: $0) }
print(dict) // ["foo": CustomObject(foo: 1), "baz": CustomObject(foo: 3)]
I'm trying to convert some C code to swift.
(Why? - to use CoreMIDI in OS-X in case you asked)
The C code is like this
void printPacketInfo(const MIDIPacket* packet) {
int i;
for (i=0; i<packet->length; i++) {
printf("%d ", packet->data[i]);
}
}
And MIDIPacket is defined like this
struct MIDIPacket
{
MIDITimeStamp timeStamp;
UInt16 length;
Byte data[256];
};
My Swift is like this
func printPacketInfo(packet: UnsafeMutablePointer<MIDIPacket>){
// print some things
print("length", packet.memory.length)
print("time", packet.memory.timeStamp)
print("data[0]", packet.memory.data.1)
for i in 0 ..< packet.memory.length {
print("data", i, packet.memory.data[i])
}
}
But this gives a compiler error
error: type '(UInt8, UInt8, .. cut .. UInt8, UInt8, UInt8)'
has no subscript members
So how can I dereference the I'th element of a fixed size array?
in your case you could try to use something like this ...
// this is tuple with 8 Int values, in your case with 256 Byte (UInt8 ??) values
var t = (1,2,3,4,5,6,7,8)
t.0
t.1
// ....
t.7
func arrayFromTuple<T,R>(tuple:T) -> [R] {
let reflection = Mirror(reflecting: tuple)
var arr : [R] = []
for i in reflection.children {
// better will be to throw an Error if i.value is not R
arr.append(i.value as! R)
}
return arr
}
let arr:[Int] = arrayFromTuple(t)
print(arr) // [1, 2, 3, 4, 5, 6, 7, 8]
...
let t2 = ("alfa","beta","gama")
let arr2:[String] = arrayFromTuple(t2)
arr2[1] // "beta"
This was suggested by https://gist.github.com/jckarter/ec630221890c39e3f8b9
func printPacketInfo(packet: UnsafeMutablePointer<MIDIPacket>){
// print some things
print("length", packet.memory.length)
print("time", packet.memory.timeStamp)
let len = Int(packet.memory.length)
withUnsafePointer(&packet.memory.data) { p in
let p = UnsafeMutablePointer<UInt8>(p)
for i:Int in 0 ..< len {
print(i, p[i])
}
}
}
This is horrible - I hope the compiler turns this nonsense into some good code
The error message is a hint: it shows that MIDIPacket.data is imported not as an array, but as a tuple. (Yes, that's how all fixed length arrays import in Swift.) You seem to have noticed this in the preceding line:
print("data[0]", packet.memory.data.1)
Tuples in Swift are very static, so there isn't a way to dynamically access a tuple element. Thus, in some sense the only "safe" or idiomatic way to print your packet (in the way that you're hinting at) would be 256 lines of code (or up to 256, since the packet's length field tells you when it's safe to stop):
print("data[1]", packet.memory.data.2)
print("data[2]", packet.memory.data.3)
print("data[3]", packet.memory.data.4)
/// ...
print("data[254]", packet.memory.data.255)
print("data[255]", packet.memory.data.256)
Clearly that's not a great solution. Using reflection, per user3441734's answer, is one (cumbersome) alternative. Unsafe memory access, per your own answer (via jckarter), is another (but as the name of the API says, it's "unsafe"). And, of course, you can always work with the packet through (Obj)C.
If you need to do something beyond printing the packet, you can extend the UnsafePointer-based solution to convert it to an array like so:
extension MIDIPacket {
var dataBytes: [UInt8] {
mutating get {
return withUnsafePointer(&data) { tuplePointer in
let elementPointer = UnsafePointer<UInt8>(tuplePointer)
return (0..<Int(length)).map { elementPointer[$0] }
}
}
}
}
Notice that this uses the packet's existing length property to expose an array that has only as many valid bytes as the packet claims to have (rather than filling up the rest of a 256-element array with zeroes). This does allocate memory, however, so it might not be good for the kinds of real-time run conditions you might be using CoreMIDI in.
Should this:
for i in 0 ..< packet.memory.length
Be this?
for i in 0 ..< packet.memory.data.length
I have a quick question about how to loop through a dictionary key and compare its key to my integer, then print out the corresponding value if the key exists.
For example, this is my code
for(var j = 0; j <= someDictionary.count; j++) {
if ( someInteger == someDictionary.keys[j] ) {
someDictioanrysValue = someDictionary.values[j]
println(someDictioanrysValue)
}
}
is there a method in dictionary that does what I want already?
If I understand what you're trying to do correctly, you're just looking to use the subscript - no for loops required.
let dict = [1: "A", 3: "B", 5: "C"]
let myInt = 3
if let value = dict[myInt] {
print(value)
}
// Prints: B
If a value doesn't exist for the key you supplied, nil will be returned and so the code inside the if let won't be executed.
If you're unfamiliar with this I'd really recommend you take a look at The Swift Programming Language.
I want the Swift version of this code:
NSArray *sortedNames = [names sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
var names = [ "Alpha", "alpha", "bravo"]
var sortedNames = names.sorted { $0.localizedCaseInsensitiveCompare($1) == NSComparisonResult.OrderedAscending }
Update: Providing explanation as per recommendation of a fellow SO user.
Unlike ObjC, in Swift you have sorted() (and sort()) method that takes a closure that you supply that returns a Boolean value to indicate whether one element should be before (true) or after (false) another element. The $0 and $1 are the elements to compare. I used the localizedCaseInsensitiveCompare to get the result you are looking for. Now, localizedCaseInsensitiveCompare returns the type of ordering, so I needed to modify it to return the appropriate bool value.
Update for Swift 2:
sorted and sort were replaced by sort and sortInPlace
Sorting an Array in Swift
Define a initial names array:
var names = [ "gamma", "Alpha", "alpha", "bravo"]
Method 1:
var sortedNames = sorted(names, {$0 < $1})
// sortedNames becomes "[Alpha, alpha, bravo, gamma]"
This can be further simplified to:
var sortedNames = sorted(names, <)
// ["Alpha", "alpha", "bravo", "gamma"]
var reverseSorted = sorted(names, >)
// ["gamma", "bravo", "alpha", "Alpha"]
Method 2:
names.sort(){$0 < $1}
// names become sorted as this --> "[Alpha, alpha, bravo, gamma]"
If your array does not contain Custom Objects (just a string or number type):
var sortedNames = sorted(names, <)
Otherwise if you create a Custom Data Object Class containing custom properties inside:
customDataObjectArray.sort({ $0.customProperty < $1.customProperty })
Most efficient way of sorting in SWIFT
The use of Operator Overloading is the most efficient way to sort Strings in Swift language.
// OPERATOR OVERLOADING
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
var sortedNames = sorted(names, <)
var reverseOrder = sorted(names, >)
In above code > and < operators are overloaded in Swift to sort Strings.
I have test the code in Playground and conclude that when we use operator overloading it is best for sorting Strings.
Copy below to Playground.
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
var reversed = sorted (names,
// This is a closure
{ (s1 : String, s2 : String) -> Bool in
return s1 > s2
}
)
println(reversed)
var reverseOrder = sorted(names, {s1, s2 in s1 > s2})
var reverseOrder2 = sorted(names, { $0 > $1} )
// OPERATOR OVERLOADING
var reverseOrder3 = sorted(names, >)
The conclusion from Playground:
From above image you can see that all other ways needs to enumerate loops for sorting 5 strings. Where as when we use Operator overloading it does not required to enumerate loop to sort strings.
Referenced from Swift documentation
If you want to sort your array in ascending order then use below syntax:
var arrayName = sorted(arrayName, <)
as the sorted() is the predefined function in swift and < is used to indicate that the array should be sorted in ascending order. If you want to sort the array in descending order then simply replace < with > as I have shown below:
var arrayName = sorted(arrayName, >)
You can usually use the built-in
func sort<T : Comparable>(inout array: [T])
but if you want to use localizedCaseInsensitiveCompare:, your code can be translated directly using NSArray.
Any method that can be used with Objective-C sortedArrayUsingSelector: can be used with Swift sort (or sorted) provided the type of thing in the array is known. So, in your code:
var arr : [String] = // ...
// it is an array of String, so we can use localizedCaseInsensitiveCompare:
sort(&arr) {return $0.localizedCaseInsensitiveCompare($1) == .OrderedAscending}
Similarly:
var events : [EKEvent] = // ...
sort(&events) {return $0.compareStartDateWithEvent($1) == .OrderedAscending}
In Swift-
let students: Set = ["Kofi", "Abena", "Peter", "Kweku", "Akosua"]
let sortedStudents = students.sorted()
print(sortedStudents)
// Prints "["Abena", "Akosua", "Kofi", "Kweku", "Peter"]"
To sort the elements of your sequence in descending order, pass the greater-than operator (>) to the sorted(isOrderedBefore:) method.
let descendingStudents = students.sorted(isOrderedBefore: >)
print(descendingStudents)
// Prints "["Peter", "Kweku", "Kofi", "Akosua", "Abena"]"