Is there an elegant way to make a custom operator that updates a dictionary value?
More specifically, I want a prefix operator that increments the integer value corresponding to a given key:
prefix operator +> {}
prefix func +> //Signature
{
...
}
var d = ["first" : 10 , "second" : 33]
+>d["second"] // should update d to ["first" : 10 , "second" : 34]
This is feasible using the functional way. For example, to calculate the frequencies of elements in an array:
func update<K,V>(var dictionary: [K:V], key: K, value: V) -> [K:V] {
dictionary[key] = value
return dictionary
}
func increment<T>(dictionary: [T:Int], key: T) -> [T:Int] {
return update(dictionary, key: key, value: dictionary[key].map{$0 + 1} ?? 1)
}
func histogram<T>( s: [T]) -> [T:Int] {
return s.reduce([T:Int](), combine: increment)
}
let foo = histogram([1,4,3,1,4,1,1,2,3]) // [2: 1, 3: 2, 1: 4, 4: 2]
But I am trying to do the same thing using a custom operator
var d = ["first" : 10 , "second" : 33]
d["second"]?++
The operator could be implemented like this:
prefix operator +> {}
prefix func +> <I : ForwardIndexType>(inout i: I?) {
i?._successorInPlace()
}
var dict = ["a":1, "b":2]
+>dict["b"]
dict // ["b": 3, "a": 1]
Although I'm not sure how it would give you a frequencies function - I mean, if it's building a dictionary, it's not going to have any keys to begin with, so there won't be anything to increment. There are a bunch of cool ways to do it, though. Using the postfix ++, you can do this:
extension SequenceType where Generator.Element : Hashable {
func frequencies() -> [Generator.Element:Int] {
var result: [Generator.Element:Int] = [:]
for element in self {
result[element]?++ ?? {result.updateValue(1, forKey: element)}()
}
return result
}
}
Airspeed Velocity tweeted another cool way:
extension Dictionary {
subscript(key: Key, or or: Value) -> Value {
get { return self[key] ?? or }
set { self[key] = newValue }
}
}
extension SequenceType where Generator.Element : Hashable {
func frequencies() -> [Generator.Element:Int] {
var result: [Generator.Element:Int] = [:]
for element in self { ++result[element, or: 0] }
return result
}
}
Or, using an undocumented function:
extension SequenceType where Generator.Element : Hashable {
func frequencies() -> [Generator.Element:Int] {
var result: [Generator.Element:Int] = [:]
for el in self {result[el]?._successorInPlace() ?? {result[el] = 1}()}
return result
}
}
First, look for a way to do it using functions (not custom operators). You want a function that takes a reference to an item (from a dictionary) and updates its value... that calls for an inout parameter type.
func increment(inout n: Int) {
n++
}
var d = ["first" : 10 , "second" : 33]
increment(&d["first"]!)
print(d) // -> "[first: 11, second: 33]"
You don't have to care about the value being in a dictionary — inout takes any kind of reference and updates it directly. (This even goes for computed properties. You can pass one inout and it'll correctly go through the setter and getter as it reads and writes values.) And because you don't have to care about the dictionary, you don't really need to be generic — if you want a function that works on dictionaries with Ints, just make a function that works on Ints and let inout do the rest.
Now, custom operators are just functions, so make an operator of your function:
prefix operator +> {}
prefix func +>(inout n: Int) {
n++
}
You can't use exactly the syntax you were asking for to invoke it, though: dictionary lookups always result in Optional types, so you have to unwrap.
+>d["second"] // error
+>d["second"]! // but this works — operators automatically make params inout as needed
print(d) // -> "[first: 11, second: 34]"
This is a little uglier than you probably are looking for, but you can accomplish it using an unsafe mutable pointer in a generic overloaded operator:
prefix operator +> {}
prefix func +><T>( value:UnsafeMutablePointer<T?> )
{
print( value.memory )
if let intValue = value.memory as? Int {
value.memory = (intValue + 1) as? T
}
}
var d = ["first" : 10 , "second" : 33]
print( d["second"] ) // Optional(33)
+>(&d["second"])
print( d["second"] ) // Optional(34)
Related
I can't find the solution to my idea. I get an error. I would like to create a big function that takes an array to change such as [1,2,3,42,342,34,3,3,2,4,5,2,3], a list with proper numbers listToFind = [1,2,3,4], and an internal function. The internal function creates a dictionary with listToFind and returns it to a big function where I want to iterate through the array, checking if the i (value) is in a dictionary.
I'm getting the error:
"Value of type '(Int) -> [Int : Bool]' has no subscripts"
after if someFunc[i] != nil {
func myFuncBig (arrayToChange: [Int], listToTakeToFind: [Int], someFunc: (Int) -> [Int:Bool]) -> [Int] {
var sortedList = [Int]()
for i in arrayToChange {
if someFunc[i] != nil {
sortedList.append(i)
}
}
return sortedList
}
func createDict (array: [Int]) -> [Int:Bool] {
var dictToReturn = [Int: Bool]()
for item in array {
dictToReturn[item] = true
}
return dictToReturn
}
It's not clear how to return a dictionary and find a value by key in it because going through the dictionary like dict[i] works without closures.
First of all you have a typo so that the someFunc parameter should be ([Int]) -> [Int:Bool] and then you only need to use this function once and return the result to a local variable. Finally you use this variable to check if a number should be included.
func myFuncBig (arrayToChange: [Int], listToTakeToFind: [Int], someFunc: ([Int]) -> [Int:Bool]) -> [Int] {
var sortedList = [Int]()
let dictionary = someFunc(listToTakeToFind)
for i in arrayToChange {
if let flag = dictionary[i], flag {
sortedList.append(i)
}
}
return sortedList
}
Note tough that the dictionary is not really needed, you could let the function return an array instead for all numbers that should be included, thus implying the true value or skip the collection all together and have a function that directly returns true or false if a given number should be included.
Finally, with the implementation of someFunc you have in the question this could have been written as
array.filter(listToFind.contains)
someFunc is a function that takes an Int and returns a dictionary, and so it should be
someFunc(i).
Try this:
func myFuncBig (arrayToChange: [Int], listToTakeToFind: [Int], someFunc: (Int) -> [Int:Bool]) -> [Int] {
var sortedList = [Int]()
for i in arrayToChange {
if !someFunc(i).isEmpty { // <-- here
sortedList.append(i)
}
}
return sortedList
}
Alternatively, you could use: if someFunc(i)[i] != nil
I'm trying to create a somewhat generic function to sort file URLs based the value of an attribute
My goal is to:
1) Pass the URL and some parameters (Including the type) to a function.
which will then loop through the file's attributes
2) Add the matching file attribute and the URL to an array of tuples
3) Sort the tuples by the value of the found attribute
4) Return the sorted array and display the items in sorted order
I believe that I need to pass the type of the attribute into the sorting function so I'm able to set it in the tuple since I'm unable to sort with "Any" but I'm unsure of how to do that
I'm okay with passing anything into the sorting function and constructing or deconstructing the value I need in the sorting function since that will be predefined depending upon what action is selected by the user
//Initial implementation to be later tied to IBActions and simplified
func sortFiles(sortByKeyString : String, fileURLArray : [URL]) -> [URL]
{
switch sortByKeyString {
case "date-created-DESC":
let fileAttributeKeyString : String = "creationDate"
let isSortOrderDesc = true
let objectTypeString : String = NSDate.className()
let sortedFileURLArray = sortFileArrayByType(fileAttributeKeyString: fileAttributeKeyString, fileURLArray: fileURLArray, type: objectTypeString, isSortOrderDesc : isSortOrderDesc)
return sortedFileURLArray
default:
return fileURLArray
}
}
//Generic function to get a files attributes from a URL by requested
type
func sortFileArrayByType(fileAttributeKeyString : String, fileURLArray : [URL], type: String, isSortOrderDesc : Bool) -> [URL] {
let fileManager = FileManager.default
let attributeToLookFor : FileAttributeKey = FileAttributeKey.init(rawValue: fileAttributeKeyString)
var tupleArrayWithURLandAttribute : [(url: URL, attribute: *Any*)]? = nil
for url in fileURLArray {
do {
let attributes = try fileManager.attributesOfItem(atPath: url.path)
for (key, value) in attributes {
if key.rawValue == fileAttributeKeyString {
tupleArrayWithURLandAttribute?.append((url: url, attribute: value))
}
}
let sortedTupleArrayWithURLandAttribute = tupleArrayWithURLandAttribute?.sorted(by: { $0.attribute < $1.attribute)})
// Need to Sort dictionary into array
return sortedTupleArrayWithURLandAttribute
} catch {
return fileURLArray
}
}
}
First read Metatype Type in the The Swift Programming Language. Once read continue with the answer.
From that you have learnt that you can declare a function parameter's type to be the type of types (you are allowed to go crosseyed), AKA metatype, and can therefore pass a type to a function. Combine that with generics and Swift's type inference and you could declare your function as:
func sortFileArrayByType<T>(fileAttributeKeyString : String,
attributeType : T.Type,
fileURLArray : [URL]
) -> [(url: URL, attribute: T)]
where T : Comparable
This adds the parameter attributeType whose type is the metatype of T where T will be inferred. For example the metatype String.self could be passed and T will be inferred to be String.
The where clause constrains T so that only types which are Comparable are allowed, this is required to enable the function to do sorting. File attributes can be Date, String and NSNumber valued; unfortunately the latter does not conform to Comparable so you need to add an extension to make it, the following will suffice:
extension NSNumber : Comparable
{
public static func <(a : NSNumber, b : NSNumber) -> Bool { return a.compare(b) == .orderedAscending }
public static func ==(a : NSNumber, b : NSNumber) -> Bool { return a.compare(b) == .orderedSame }
}
Within the body of the function you need to declare your array of tuples to have attributes of type T:
var tupleArrayWithURLandAttribute : [(url: URL, attribute: T)] = []
and when you add entries you need to cast the value returned by attributesOfItem to be T:
tupleArrayWithURLandAttribute.append((url: url, attribute: value as! T))
Note the use of as! here, you must match the attribute name and the type of its value correctly in the function call or you will get a runtime abort. Handling this as a soft error, if needed, is left as an exercise.
There are a number of typos etc. in the code you posted, they are left for you to fix, having done that your function should work. A call might look like:
let ans = sortFileArrayByType2(fileAttributeKeyString: "NSFileCreationDate",
attributeType: Date.self,
fileURLArray: urlArray)
and the type of ans in this case will be [(url: URL, attribute: Date)]
HTH
So I think I know what you're getting at and this is what I've come up with:
func checkType<T>(_ type: T.Type) {
if type.self == String.self {
print("It's a string!")
} else if type.self == Int.self {
print("It's an int!")
} else {
print("It's something else...")
}
}
And then you can call this either by passing in a type directly to it, or by getting the type of a variable and passing that in as follows:
checkType(String.self) // prints "It's a string!"
let number: Int = 1
checkType(type(of: number)) // prints "It's an int!"
Hope this helps!
What you're looking for here is a way to sort a sequence of URLs by a URLResourceKey (and specifically by the URLResourceValues property related to that key). Unfortunately, URLResourceValues aren't mapped to URLResourceKey in a useful way. But we can fix that with an extension:
extension URLResourceValues {
static func key<T>(for keyPath: KeyPath<Self, T>) -> URLResourceKey {
switch keyPath {
case \Self.creationDate: return .creationDateKey
// ... Other keys ...
default: fatalError()
}
}
}
And it would be very useful to get a value for a URLResourceValues keyPath:
extension URL {
func resourceValue<T>(for keyPath: KeyPath<URLResourceValues, T?>) throws -> T? {
return try resourceValues(forKeys: Set([URLResourceValues.key(for: keyPath)]))[keyPath: keyPath]
}
}
With that, we can build a sorting method based on URLResourceValues (assuming nil is less than other values; you could replace that with throwing for non-existent values):
extension Sequence where Element == URL {
func sorted<T>(by keyPath: KeyPath<URLResourceValues, T?>) throws -> [URL]
where ResourceType: Comparable {
return try self
.sorted { (lhs, rhs) in
guard let lhsValue = try lhs.resourceValue(for: keyPath)
else { return true }
guard let rhsValue = try rhs.resourceValue(for: keyPath)
else { return false }
return lhsValue < rhsValue
}
}
}
And finally, that can be used by passing a keypath, based on URLResourceValues:
let sortedFiles = try files.sorted(by: \.creationDate)
I want to test my function that takes a string, a returns all the pairs of characters as an array s.t.
func pairsOfChars(_ s: String) -> [(Character,Character)] {
let strArray = Array(s)
var outputArray = [(Character,Character)]()
for i in 0..<strArray.count - 1 {
for j in i + 1..<strArray.count {
outputArray.append( (strArray[i], strArray[j]) )
}
}
return outputArray
}
So I want to create a suite of tests using XCTestCase. I usually use XCTestCase and XCTAssertEqual but these are only appropriate for C scalar types. This means that the following test case returns an error:
class pairsTests: XCTestCase {
func testNaive() {
measure {
XCTAssertEqual( pairsOfChars("abc") , [(Character("a"),Character("b")),(Character("a"),Character("c")),(Character("b"),Character("c")) ] )
}
}
}
I could convert to a string, but I'm thinking there is a better solution.
How can I test an output of an array of pairs of characters [(Character,Character)]
Your notion of a nonscalar is a total red herring. The problem is one of equatability.
How can I test an output of an array of pairs of characters [(Character,Character)]
You can't, because there is no default notion of what it would mean to equate two such arrays. This is the old "tuples of Equatable are not Equatable" problem (https://bugs.swift.org/browse/SR-1222) which still rears its head with arrays. The == operator works on tuples by a kind of magic, but they are still not formally Equatable.
You could define equatability of arrays of character pairs yourself:
typealias CharPair = (Character,Character)
func ==(lhs:[CharPair], rhs:[CharPair]) -> Bool {
if lhs.count != rhs.count {
return false
}
let zipped = zip(lhs,rhs)
return zipped.allSatisfy{$0 == $1}
}
Alternatively, have your pairsOfChars return something that is more easily made equatable, such as an array of a struct for which Equatable is defined.
For example:
struct CharacterPair : Equatable {
let c1:Character
let c2:Character
// in Swift 4.2 this next bit is not needed
static func ==(lhs:CharacterPair, rhs:CharacterPair) -> Bool {
return lhs.c1 == rhs.c1 && lhs.c2 == rhs.c2
}
}
func pairsOfChars(_ s: String) -> [CharacterPair] {
let strArray = Array(s)
var outputArray = [CharacterPair]()
for i in 0..<strArray.count - 1 {
for j in i + 1..<strArray.count {
outputArray.append(CharacterPair(c1:strArray[i],c2:strArray[j]))
}
}
return outputArray
}
You would then rewrite the test to match:
XCTAssertEqual(
pairsOfChars("abc"),
[CharacterPair(c1:Character("a"),c2:Character("b")),
CharacterPair(c1:Character("a"),c2:Character("c")),
CharacterPair(c1:Character("b"),c2:Character("c"))]
)
I am trying to write or implement my own higher order function my way, but not able to write it.
In below code I tried to write filter.
var array : [String] = ["Sagar","Harshit","Parth","Gunja","Marmik","Sachin","Saurav"]
//Native filter function of Swift
array = array.filter { (name) -> Bool in
return name.prefix(1) == "S"
}
I implement below code, according to method signature of filter, but as I know , we can not write closure with return type(If possible then I don't know).
func filterArray(_ array : [String], completionHandler : (_ name : String) -> ()) -> (){
for (_, value) in array.enumerated(){
completionHandler(value)
}
}
self.filterArray(array) { (name) -> () in
if name.prefix(1) != "S"{
if let index = array.index(of: name){
array.remove(at: index)
}
}
}
My implementation working fine and filtering array. But I want to abstract logic of remove object from array.
Can we write our own higher order functions or not ?
If yes then please help to implement above one.
Thanks in advance.
And you can define a return type to a closure. You can find a working example below, but for this purpose I suggest using the Swift built in filter function which can provide the same solution and much faster.
var array : [String] = ["Sagar","Harshit","Parth","Gunja","Marmik","Sachin","Saurav"]
func filterArray(_ array : inout [String], condition: (_ name : String) -> Bool) -> (){
var filteredArray: [String] = []
for value in array {
if condition(value) {
filteredArray.append(value)
}
}
array = filteredArray
}
filterArray(&array) { (name) -> Bool in
return !name.hasPrefix("S")
}
print(array)
You can define your own higher order functions on collections.
There is a great session about collections where Soroush shows an example of writing your own higher order function extending a collection.
https://academy.realm.io/posts/try-swift-soroush-khanlou-sequence-collection/
// Swit built in filter
let numberOfAdmins = users.filter({ $0.isAdmin }).count // => fine
// Custom "filter"
let numberOfAdmins = users.count({ $0.isAdmin }) // => great
extension Sequence {
func count(_ shouldCount: (Iterator.Element) -> Bool) -> Int {
var count = 0
for element in self {
if shouldCount(element) {
count += 1
}
}
return count
}
}
I have this code
for (k, v) in myDict {
println(k)
}
How do I access the next key in the dictionary (e.g. myDict[k + 1])?
Thanks in advance!
There is no such thing as "the next key"; dictionaries have no order.
Since, however, you are iterating through the dictionary...
for (k, v) in myDict {
println(k)
}
I'm going to assume that what you mean is: how can I know, on this iteration, what k would be on the next iteration?
A simple solution would be to coerce the dictionary to an array (of key-value tuples):
let arr = Array(myDict)
Now you have something with integer indexes. So you can enumerate it like this:
let arr = Array(myDict)
for (ix, (k,v)) in enumerate(arr) {
println("This key is \(k)")
if ix < arr.count-1 {
println("The next key is \(arr[ix+1].0)")
}
}
The truth is, of course, that you can enumerate a dictionary directly, but indexes are not integers, so they are a little harder to work with. Martin R is also showing an approach illustrating that point.
I don't know if this is what you are looking for, but you can
iterate through a dictionary in a "similar" way as iterating
through an array by using the DictionaryIndex<Key, Value> as an index:
let dict = [ "foo" : 1, "bar" : 2, "baz" : 3]
for idx in indices(dict) {
let (k, v) = dict[idx]
println("Current key: \(k), current value: \(v)")
let nextIdx = idx.successor()
if nextIdx != dict.endIndex {
let (k1, v1) = dict[nextIdx]
println("Next key: \(k1), next value: \(v1)")
}
}
Sample output:
Current key: bar, current value: 2
Next key: baz, next value: 3
Current key: baz, current value: 3
Next key: foo, next value: 1
Current key: foo, current value: 1
A possible solution is to create Generator which returns the current and previous values in a sequence. For this you need a custom Generator which will return a tuple, containing the previous and current values from a sequence, from next:
struct PairGenerator<Base: GeneratorType> : GeneratorType {
typealias ElementPair = (previousElement: Base.Element, currentElement: Base.Element)
private var base: Base
private var previousElement: Base.Element?
init(_ base: Base) {
self.base = base
}
mutating func next() -> ElementPair? {
if previousElement == nil { previousElement = base.next() }
let currentElement = base.next()
// Since `base.next()` returns `nil` when the end of the sequence
// is reached, we need to check `previousElement` and `currentElement `
// aren't `nil`. If either of them are, `nil` will be returned to signal
// there aren't any pairs left.
if let prev = previousElement, curr = currentElement {
previousElement = currentElement
return (prev, curr)
}
return nil
}
}
The PairGenerator is then stored in a PairSequence, which conforms to SequenceType; this means you can iterate over it in a for loop.
struct PairSequence<Base: SequenceType> : SequenceType {
let generator: PairGenerator<Base.Generator>
init(_ base: Base) {
generator = PairGenerator(base.generate())
}
func generate() -> PairGenerator<Base.Generator> {
return generator
}
}
Now you need a function which will create a PairSequence from an object that conforms to SequenceType:
func pairs<Seq: SequenceType>(base: Seq) -> PairSequence<Seq> {
return PairSequence(base)
}
Finally, you can use this like so:
let myDict = ["1": 1, "2": 2, "3": 3, "4": 4]
let values = Array(myDict.values).sorted(<)
for (prev, curr) in pairs(values) {
println("\(prev), \(curr)")
}
// Prints:
// 1, 2
// 2, 3
// 3, 4
You could use pairs(myDict), but like #Martin R and #matt said - Dictionaries don't have an order so you may not get the results in the order you expected.
For more information on SequenceType and GeneratorType, I'd recommend looking at Playing With Swift and Generators In Swift.
Or, as #Martin R pointed out in his comment, you could use:
for (prev, curr) in zip(values, dropFirst(values)) {
println("\(prev), \(curr)")
}