Swift 3: Getting error while joining the reversed() array - arrays

I have code like this:
class Stack<T>: CustomStringConvertible {
fileprivate var array: [T] = []
func pop() -> T? {
let popItem = self.array.popLast()
print("POP ITEM : \(popItem)")
return popItem
}
func push(item: T) {
print("PUSH ITEM : \(item)")
array.append(item)
}
var isEmpty: Bool {
return self.array.isEmpty
}
var count: Int {
return self.array.count
}
var description: String {
let topDivider = "### STACK ###\n"
let bottomDivider = "\n############\n"
let stackElements = self.array.reversed().joined(separator: "\n") // GETTING ERROR
return topDivider + stackElements + bottomDivider
}
}
Error: Type of expression is ambiguous without more context
I'm unable to join that reverse array.
REF : https://www.raywenderlich.com/149213/swift-algorithm-club-swift-stack-data-structure
Here they have done in struct with String successfully.
Can i know How i can achieve same here?

The problem is that the joined function requires that the separator be the same data type as the values being joined. Since self.array is an array of T, you can't pass a String as the separator. This mismatch is causing the error.
Since this is your description property and your goal is to get a string representation of the array, one solution would be to map the array of T to an array of String. Then you can join that String array with your newline separator.
One way to convert something to a String is to call the description method on it. But to do that, you need to limit your T to only those types that provide the description method which comes from CustomStringConvertible.
Change your class declaration to:
class Stack<T:CustomStringConvertible>: CustomStringConvertible {
and then your description property to:
var description: String {
let topDivider = "### STACK ###\n"
let bottomDivider = "\n############\n"
let stackElements = self.array.reversed().map { $0.description }.joined(separator: "\n")
return topDivider + stackElements + bottomDivider
}

Map your generic type T to String and then apply joined() function.
joined is only for elements of type String and here compiler do not know the type of T.
let stackElements = self.array.map{"\($0)"}.reversed().joined(separator: "\n")

Related

How do I return an array?

I need to write an app that takes the string that is entered into a text field and use it to call a thesaurus function. The function must return an array that contains synonyms to the string, but I seem to be having some syntax issues. Can someone lend an extra pair of eyes?
I already checked on the syntax and the scope of the variables, but I don't seem to understand where I'm getting it wrong.
var synonymsDictionary = ["swift" : ["abrupt", "expeditious", "hasty", "nimble", "quick", "rapid", "speedy", "sudden", "unexpected"],
"objective" : ["detached", "disinterested", "dispassionate", "equitable", "evenhanded", "nonpartisan", "open-minded", "unbiased"],
"calculate" : ["adjust", "appraise", "consider", "count", "determine", "forecast", "gauge", "guess", "measure", "multiply", "reckon", "subtract", "tally", "weigh", "work out"],
"create" : ["build", "conceive", "constitute", "construct", "design", "devise", "discover", "establish", "forge", "form"]]
func synonyms(for term: String) -> String {
if let sameWords = synonymsDictionary[term] {
print("These are the synonyms for \(term): \(sameWords)")
} else {
print("This word doesn't have any synonyms.")
}
let result = synonymsDictionary[term]
return result
}
synonyms(for: "objective")
I should be getting an array with the synonyms for the term (string ) that I put in.Error
Cannot convert return expression of type '[String]?' to return type 'String'
Currently you return String but it should be an array [String] so try
func synonyms(for term: String) -> [String] {
return synonymsDictionary[term] ?? []
}
Also hold a reference to the returned value
let res = synonyms(for: "objective")
print(res)
Since synonymsDictionary is a dictionary [String:[String]] then every value is of type [String] that can't be returned in a function that returns a value of type String

Cast to right generic from array in Swift

I have a Protocol called Composite.
This protocol has an array composites: [Composite]
I also have a generic subclass GenericSubclass<T>: Composite
When iterating over the array the best I can come up with looks like this:
for item in composites {
if let item = item as? GenericSubclass<A> {
let sc = SomeOtherClass<A>
} else if let item = item as? GenericSubclass<B> {
let sc = SomeOtherClass<B>
} //and so on...
}
Is there any way to get a hold of GenericSubclass without specifying the Generic? In my use case there is absolutely no need for me to know about the T. I just have to instantiate another class with the same generic type.
Any help is much appreciated.
It's not clear what you're trying to accomplish with the "generic" (pun intended) class names you've chosen. I don't think there's a way to directly accomplish what you want. I.e. you can't just leave it as a generic T because the compiler needs some way to determine what T will be in use at runtime.
However, one way to solve the issue is to hoist the API into the Composite protocol:
protocol Composite {
var composites: [Composite] { get set }
func otherClass() -> OtherProtocol
}
protocol OtherProtocol { }
class GenericSubclass<T>: Composite {
var composites: [Composite] = []
func otherClass() -> OtherProtocol {
return SomeOtherClass<T>()
}
}
class SomeOtherClass<T>: OtherProtocol {}
So now when you implement your loop, you can rely on the fact that since each element is a Composite, you know it must provide an instance of OtherProtocol via the otherClass() method:
var c = GenericSubclass<Int>()
c.composites = [GenericSubclass<Double>(), GenericSubclass<Int>(), GenericSubclass<Character>()]
for item in c.composites {
let sc = item.otherClass()
print(sc)
}
Alternatively, if only GenericSubclass should vend an OtherProtocol, you can make the return type Optional and define an extension for all the other implementations of Composite:
protocol Composite {
var composites: [Composite] { get set }
func optionalClass() -> OtherProtocol?
}
extension Composite {
func optionalClass() -> OtherProtocol? {
return nil
}
}
I did some experiment on this in the playground and i came up with this
protocol Composite {
var composites: [Composite] { get set }
}
class GenericSubclass<T>: Composite {
var composites: [Composite] = []
}
let subclass = GenericSubclass<String>()
for item in subclass.composites {
let className = String(describing: type(of: item))
let aClassType = NSClassFromString(className) as! NSObject.Type
let instance = aClassType.init() // we create a new object
print(instance) //Output: GenericSubclass<String>
}
Hope this will help someone.
I think it's not possible to do that in array.
While you creat some different GenericSubclass<T> then put it in array , you will lose <T> no matter the composites is [Composite] or [Any].
// this line won't compile
let array = [GenericSubclass<Int>(),GenericSubclass<Double>()]
//error: heterogenous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional
You want donging something like this func below, the param should be GenericSubclass<T> to compile success
func genericFunc<T>(param:GenericSubclass<T>) {
let sc = SomeOtherClass<T>()
print(sc)
}
Anyway you can implement it with member var for the instance like the code below:
class Subclass {
var type : Any
init(type : Any) {
self.type = type
}
}
class SomeOtherClass : CustomDebugStringConvertible{
var type : Any
init(type : Any) {
self.type = type
}
var debugDescription: String{
return String(describing: type.self)
}
}
let array : [Subclass] = [Subclass(type : Int.self),Subclass(type : Double.self),Subclass(type : String.self)]
let scArray = array.flatMap {SomeOtherClass(type:$0.type.self)}
print(scArray) // prints [Int, Double, String]
You need to add one method to protocol which creates new item of Type supported this protocol. So now you can use enums, structs and classes without any knowledge of creating object of specific type.
You can play in playground with the following code:
import UIKit
//This is your protocol
protocol MyAwesomeProtocol {
//this methods leaves implementaion detailes
//to concrete type
func createNewObject()->MyAwesomeProtocol
}
//Just create empty string
extension String: MyAwesomeProtocol {
func createNewObject() -> MyAwesomeProtocol {
return String()
}
}
//create Enum with default value
extension UIControlState: MyAwesomeProtocol {
func createNewObject() -> MyAwesomeProtocol {
return UIControlState.normal
}
}
//create viewController of any type
extension UIViewController: MyAwesomeProtocol {
func createNewObject() -> MyAwesomeProtocol {
return type(of:self).init()
}
}
//This is test function
//it creates array of newly created items and prints them out
//in terminal
func doSomeCoolStuffWith(items:[MyAwesomeProtocol]){
var newItems = [MyAwesomeProtocol]()
for anItem in items {
let newOne = anItem.createNewObject()
newItems.append(newOne)
}
print("created new ones:\n\(newItems)\nfrom old ones:\n\(items)\n")
}
doSomeCoolStuffWith(items: [UIControlState.focused,UIControlState.disabled])
doSomeCoolStuffWith(items: [UISplitViewController(),UINavigationController(),UICollectionViewController()])
doSomeCoolStuffWith(items: ["I","love","swift"])
This will produce the following result:
created new ones:
[__C.UIControlState(rawValue: 0), __C.UIControlState(rawValue: 0)]
from old ones:
[__C.UIControlState(rawValue: 8), __C.UIControlState(rawValue: 2)]
created new ones:
[<UISplitViewController: 0x7fa8ee7092d0>, <UINavigationController: 0x7fa8f0044a00>, <UICollectionViewController: 0x7fa8ee705f30>]
from old ones:
[<UISplitViewController: 0x7fa8ee7011e0>, <UINavigationController: 0x7fa8f004e600>, <UICollectionViewController: 0x7fa8ee708fb0>]
created new ones:
["", "", ""]
from old ones:
["I", "love", "swift"]

Instance member cannot be used on type Array

I have the below class and I use a function math to search the string in both SongTitle and in ESongTitle.
class KeerthanaiArray: NSObject {
var SongTitle: String = String()
var SongLyrics: String = String()
var ESongTitle: String = String()
init(SongTitle: String, SongLyrics:String, ESongTitle: String) {
self.SongTitle = SongTitle
self.SongLyrics = SongLyrics
self.ESongTitle = ESongTitle
}
class func match(string:String) -> Bool {
return SongTitle.containsString(string) || ESongTitle.containsString(string)
}
}
I get the error message 'Instance member SongTitle cannot be used on type 'Keerthanaiarray'. Please help
I need to declare the math func as class as I need to use the math function outside of its class
There are several problems here.
This class KeerthanaiArray is not an array (as suggested by the name instead)
Why are you extending NSObject?
The class method match makes no sense, it is using 2 properties (SongTitle and ESongTitle) that does not exists in this context because they belongs to an instance of the class.
So let's cleanup your code
struct Song {
let title: String
let lyrics: String
let eTitle: String
func match(keyword: String) -> Bool {
return title.containsString(keyword) || eTitle.containsString(keyword)
}
}
I make you class a struct because makes more sense. You are free to turn back to class. If you stay on structs please keep in mind they are value types.
Now given a list of Song(s)
let songs: [Song] = ...
and a keyword
let keyword = "The Great Gig In the Sky"
this is how we search the array
let results = songs.filter { $0.match(keyword) }
Case insensitive version
struct Song {
let title: String
let lyrics: String
let eTitle: String
func match(keyword: String) -> Bool {
let lowerCaseKeyword = keyword.lowercaseString
return title.lowercaseString.containsString(lowerCaseKeyword) || eTitle.lowercaseString.containsString(lowerCaseKeyword)
}
}

Store generic Arrays in NSUserDefaults

I'm trying to store a generic Array in NSUserDefaults but I get the following error: Cannot convert value of type 'Array<T>' to expected argument type 'AnyObject?'.
How can I solve this problem?
public class PropertyStore {
private let userDefaults = NSUserDefaults.standardUserDefaults()
public func loadSet<T>(key: String) -> Set<T>? {
guard let array = userDefaults.objectForKey(key) as? [T] else {
return nil
}
return Set<T>(array)
}
public func saveSet<T>(key: String, value: Set<T>) {
let array = Array(value)
userDefaults.setObject(array, forKey: key) // <- ERROR
}
}
Like #lucasD said, T needs to conform to NSCoding atleast. So the code looks like this.
public func saveSet<T: NSCoding>(key: String, value: Set<T>) {
let array = Array(value)
userDefaults.setObject(array, forKey: key)
}
However, this will not work for many reasons like:
public func loadSet<T: NSCoding>(key: String) -> Set<T>? {
guard let array = userDefaults.objectForKey(key) as? [T] else {
return nil
}
return Set<T>(array)
}
let defaults = PropertyStore()
defaults.saveSet("array", value: [1,2,3])
///defaults.loadSet<Int>("array") ===> Cannot explicitly specialize a function
///defaults.loadSet("array") ===> Cannot infer Type T
//let a: Set<Int>? = defaults.loadSet("array") ==> T cannot be inferred
In the case of loadSet type T cannot be inferred properly because we cannot specify it from outside, as far as i know. I would first try to return NSObject or Set<AnyObject> or Set<NSCoding> and then type cast it explicitly. Let me know if theres a better way though.
You can take a look at this SO post for more information on why a generics parameter cannot be specialised from outside. SO Generics specialization
You should define T as NSCoding conforming class. So when you're going to store the array, you are going to store an array of NSKeyedArchiver.archiveDataWithRootObject() results. Then, to return a Set<T> in the loadSet method, you should unarchive all the objects:
let storedData = NSUserDefaults.standardUserDefaults. ...
return storedData.flatMap { return NSKeyedUnarchiver.unarchiveObjectWithData($0) }

Cannot convert value of type 'Meme!' to expected argument type '#noescape (Meme) throws -> Bool'

Here is the code:
#IBAction func deleteMeme(sender: UIBarButtonItem) {
if let foundIndex = MemeRepository.sharedInstance.memes.indexOf(selectedMeme) {
//remove the item at the found index
MemeRepository.sharedInstance.memes.removeAtIndex(foundIndex)
navigationController?.popViewControllerAnimated(true)
The error happens at the .indexOf method at (selectedMeme).
Cannot convert value of type Meme! to expected argument type #noescape (Meme) throws -> Bool
Meme! is a struct for my app. How do I work through this?
struct Meme {
var topText : String!
var bottomText: String!
var image: UIImage!
var memedImage: UIImage!
init(topText: String, bottomText: String, image: UIImage, memedImage: UIImage) {
self.topText = topText
self.bottomText = bottomText
self.image = image
self.memedImage = memedImage
The error message is misleading. What you actually need is to provide the compiler a way to compare two Meme instances and decide upon which criteria those instances are equal.
Let's say you want two instances having the same name property to be treated as equal.
We make the struct conform to Equatable and we also create an == operator that compares two structs by their name property:
struct Meme:Equatable {
var name:String!
}
func ==(lhs: Meme, rhs: Meme) -> Bool {
return lhs.name == rhs.name
}
Now we can use indexOf with a Meme instance:
let doge = Meme(name: "doge")
let lolcat = Meme(name: "lolcat")
let memes = [lolcat, doge]
if let dogeIndex = memes.indexOf(doge) {
print(dogeIndex) // 1
}
If you wanted to compare two instances not by their name property but by their uniqueness, then you would have to make the struct conform to Hashable and use a unique hashValue property returning an Int:
struct Meme:Hashable {
var name:String!
var hashValue: Int {
return self.name.hashValue
}
init(name: String) {
self.name = name
}
}
func ==(lhs: Meme, rhs: Meme) -> Bool {
return lhs.hashValue == rhs.hashValue
}
let doge = Meme(name: "doge")
let lolcat = Meme(name: "lolcat")
let memes = [lolcat, doge]
if let dogeIndex = memes.indexOf(doge) {
print(dogeIndex) // 1
}
In this example the hashValue is made from self.name, so two different instances of Meme with a same name property will be considered equal. If you don't want that, use another source for the hash value.
Note: in Swift 3, indexOf has become index(of:), so for this example we would change memes.indexOf(doge) to memes.index(of: doge).
If you want to put the comparison inside the indexOf method itself, do it like this:
if let foundIndex = MemeRepository.sharedInstance.memes.indexOf({
UIImagePNGRepresentation($0.memedImage) == UIImagePNGRepresentation(selectedMeme.memedImage)})
Probably not the best way to compare images. If you know the images are the same object, you can use:
.indexOf({$0.memedImage == selectedMeme.memedImage})
but if you want to compare them pixel by pixel or compare the same image scaled to different sizes, that is a little more complicated.

Resources