I have an extension for an Array:
extension Array where Element == [String:Double] {
func values (keyOrder : [String]) -> [[Double]] {
return self.map { element in
return (0..<keyOrder.count).compactMap {element[keyOrder[$0]]}
}
}
}
It works pretty well, but only if Dictionary Key is String and Value is Double. I can imagine this function could work exactly same way for Dictionary of any types, like [AnyHashable:Any] but I have no clue how to define header, is it possible?
One useful trick you can use in situations like this is to move the where clause from the extension declaration to the method declaration. This allows you to introduce new generic placeholders for the nested dictionary's Key and Value placeholder types:
extension Array {
func nestedValues<Key, Value>(orderedBy keys: [Key]) -> [[Value]] where Element == [Key: Value] {
return map { element in
return keys.compactMap { element[$0] }
}
}
}
Use can use value of dictionary double as an Any Type. You can try below code.
extension Array where Element == [String: Any] {
func values (keyOrder : [String]) -> [[Any]] {
return self.map { element in
return (0..<keyOrder.count).compactMap {element[keyOrder[$0]]}
}
}
}
Related
My first attempt to use swift generics:
extension RealmSwift.List where Element == Object {
// #deprecated use RealmSwift.List<>
func arrayo<T: Object>() -> [T] {
var res: [T] = []
for card in self {
res.append(card) <- here I got
No exact matches in call to instance method 'append'
}
return res
}
convenience init<T: Object>(objects: [T]) {
self.init()
for card in objects {
append(card)
}
}
}
what's a good way to write this adapter once and for all?
Notice the where Element. You can refer to the type of the list items using Element, so you do not need to set up another type parameter T. card is of type Element not T, so you cannot add it to the Array<T>. There is no guarantee that T and Element are equivalent so the compiler doesn't allow it. The same applies for your convenience init.
extension RealmSwift.List where Element == Object {
// #deprecated use RealmSwift.List<>
func arrayo() -> [Element] {
var res: [Element] = []
for card in self {
res.append(card) // Now you are adding an `Element` to the array of `Element` so it will work.
}
return res
}
convenience init(objects: [Element]) {
self.init()
for card in objects {
append(card)
}
}
}
But generics are not really useful here because you are constraining Element to Object already. So there is only one potential type - You could make arrayo() and the init use Object directly.
To make this useful do
extension RealmSwift.List where Elemtn: RealmCollectionValue
I want to write a generic method in an Array extension which takes a parameter type that is also an array where the element types are the same (for the caller of the array extension and the parameter). This is kind of what I mean (but none of them works):
extension Array {
func doSomethingWithAnotherArray(arr: Self) {
}
}
extension Array {
func doSomethingWithAnotherArray<T: Array<Element>>(arr: T){
}
}
extension Array {
func doSomethingWithAnotherArray<T: Array<U>, U>(arr: T) where U == Element{
}
}
So I can use it as:
let x = [1, 2]
let y = [3, 4]
x.doSomethingWithAnotherArray(arr: y)
Since x and y has the same elements.
Just simply pass parameter of type Array
extension Array {
func doSomethingWithAnotherArray(arr: Array) {
... // do something
}
}
[Int].doSomethingWithAnotherArray(arr: [Int]) // works
[Int].doSomethingWithAnotherArray(arr: [String]) // doesn't work
If the only restriction is that Element of the argument is the same as that of the receiver:
extension Array {
func doSomethingWithAnotherArray(arr: Array<Element>) {
// …
}
}
edit: As seen in this answer, a simple Array suffices, since this function is not generic in itself, and in context of the generic type, Array is already specialized to the type of the receiver.
If you need to place other restrictions on Element, use extension Array where Element ….
extension Array where Element: StringLiteralConvertible{
func spliteByPrefix() -> [Element]{
for item in self{
}
return []
}
}
I want to write an extension of Array whose Element is always a String. And in my spliteByPrefix() function, i want to use maybe item.characters or something else which a String has. How?
As for now, you cannot write an extension of Array "whose Element is always a String" in Swift.
But you can write some extension which has nearly the same functionality.
Write your own protocol, which String can conform to:
protocol MyStringType {
var characters: String.CharacterView { get }
//You may need some other properties or methods to write your extension...
}
// Make `String` the only type conforming to `MyStringType`.
extension String: MyStringType {}
And write an extension where Element conforms to the protocol.
extension Array where Element: MyStringType {
func spliteByPrefix() -> [Element]{ //You really want to return, `Array<Element>`?
for item in self {
for ch in item.characters {
//Do something with `ch`.
_ = ch
}
}
return []
}
}
In this example Element is always a Int. I use the elements themselves of the array
extension Array where Element: IntegerType {
func toString() -> [String] {
var result = [String]()
for value in self {
result.append(String(value))
}
return result;
}
}
Example of use:
let numberInt = [23, 10, 79, 3]
let numberString = numberInt.toString()
I have a let map : [String: String] and a let key: String?.
What is the most concise way to access map[key] (and get back a String? if I had a key and None if I did not)?
let value = key.flatMap { map[$0] }
would to the trick, using the
/// Returns `nil` if `self` is nil, `f(self!)` otherwise.
#warn_unused_result
public func flatMap<U>(#noescape f: (Wrapped) throws -> U?) rethrows -> U?
method from struct Optional.
Alternatively, you can wrap that into a custom subscript method
extension Dictionary {
subscript(optKey : Key?) -> Value? {
return optKey.flatMap { self[$0] }
}
}
and the simply write
let value = map[key]
To avoid confusion with the "normal" subscript method, and to make
the intention more clear to the reader of your code, you can define
the subscript method with an external parameter name:
extension Dictionary {
subscript(optional optKey : Key?) -> Value? {
return optKey.flatMap { self[$0] }
}
}
let value = map[optional: key]
I think I am going with the decidedly unfancy
key == nil ? nil : map[key!]
I want to write a function named remove in Swift which will accept an array or a string and remove the string from a dictionary if it is a string else it will remove all the strings from the dictionary which are present in the array. Also, this function that I wrote is disabling the styles in the editor of Xcode.
func remove(key: AnyObject){
if key is Array{
for (index, value) in enumerate(key){
if -1 < self._getDataStoreKeyIndex(value){
self._removeProperty(value)
} else{
self._removeItem(value)
}
}
}else{
if -1 < self._getDataStoreKeyIndex(key){
self._removeProperty(key)
}else{
self._removeItem(key)
}
}
}
The other functions in the code are correct because if I comment this function my project is building successfully. What is wrong in my code?
I am getting a segmentation fault.
<unknown>:0: error: unable to execute command: Segmentation fault: 11
The heart of the problem is attempting to convert to "Array" which is a generic class. Instead, you need to convert to a specific instantiation of the generic, in your case "Array" or "[String]"
You also have a problem in that you can't enumerate AnyObject, combined, you need something like:
func remove(key: AnyObject) {
if let array = key as? Array<String> {
for (index, value) in enumerate(array) {
}
}
else {
}
}
Although I'm not sure why you're using enumerate to get at the indices which you don't use for anything, faster and more legible to just iterate the array directly:
func remove(key: AnyObject) {
if let array = key as? Array<String> {
for value in array {
}
}
else {
}
}
One further thought... these two operations really aren't similar and don't really share much code. Why not define to different functions that differ in signature:
func remove(key:String) {
}
func remove(array:[String]) {
for string in array {
remove[string]
}
}
Which eliminates the possibility that somebody calls "remove(5)"