I have an array of Strings and I want to use that as a list.
I followed this example but I didn't get for an array. Link
My current code is given below:
struct ListOfPeripherals: Identifiable {
var id = UUID()
var peripheralName: String
}
struct RestaurantRow: View {
var peripheralFromBLE: ListOfPeripherals
var body: some View {
// List to be implemented here
}
func getListOfAlphabets() -> [String] {
let listOfAlphabets = [A,B,C,D,E]
return listOfAlphabets
}
}
You dont have to convert your Array of String objects to make the List work. You should describe your error pasting it along with your code to get helped faster. So, this is my assumption of your concept of error.
The Array type is fine, but however, his Elements, in this case the strings, need to be identified. This means you have to conform your String to the Identifiable protocol, and providing an id property which must be unique (a simple UUID object will do the trick).
In your code, the function getListOfAlphabets returns an array of Strings which cannot be used. If you were to use a [ListOfPeripherals] array that would work because it conforms to the protocol. I think you got a bit of confusion and you are basically returning an array of wrong type.
Related
I want to parse one json than have an array with multiple strings, but i don't know how to do. I know how to parse json but in static method but i don't know if I've multiple string
My json is:
{
"index": [
{
"numberOfString":"N"
"string1":"myString1",
"string2":"myString2",
"stringN":"mystringN"
}
]
}
If the number of strings is unknown or can change you can decode this to a dictionary. The struct for this would be:
struct Response: Codable{
var index: [[String:String]]
}
In your example the index var contains an array of [String:String].
Edit:
As you asked for an example:
I don´t know what you want to do with this and don´t know where this json comes from (file/dataStream) so it will be just a general example.
Let´s say you get this as Data then the next step would be to decode it.
let response = try JsonDecoder().decode(Response.self, from: data)
this will lead to an object with a structure described in my initial answer. Now you can iterate over the dictionary in the response and map the values to an array for example. This could be used in a tableview for presenting.
let arr = response.index.map{ $1 }
I have an array of object, each object contains a discount rate , I need to sort them increasingly by their rate,
struct ShopDetails {
var shopId: Int?
var discountRate: String?
init(with json: Dictionary<String,Any>) {
shopId = json["id"] as? Int
discountRate = json["discount"] as? String
}
I tired to sort them using this method;
func getShopsByDiscount() {
let sortedImages = self.copyOfShops.sorted(by: { (shop1: ShopDetails, shop2: ShopDetails) -> Bool in
return Int(shop1.discountRate) < Int(shop2.discountRate)
})
}
I tried to cast the rate to integer, since its been received form the backend as string, but I got an error:
value of type Any has no member discountRate.
any idea how to do it? if there is a way to do it without casting it will be better
First, you need to verify that the array you're starting with is of type [ShopDetails]. The error indicates that this is probably an Objective-C NSArray, which won't work as well in Swift. If you're unclear about this, I suggest you Google the topic: there's no reason to use NSArray in Swift.
Below, I assume the array is the correct type ([ShopDetails]). From here, you need to do two additional things, because discountRate is of type String?.
You need to check if the string is actually there
You need to check if it can actually be expressed as an Int.
With these things in mind, your sort function can look like this:
let sortedImages = copyOfShops.sorted(by: {
(shop1: ShopDetails, shop2: ShopDetails) -> Bool in
if let shop1String = shop1.discountRate, let shop1Value = Int(shop1String),
let shop2String = shop2.discountRate, let shop2Value = Int(shop2String) {
return shop1Value < shop2Value
}
return true
})
That being said, the best way to handle this is to change the type of discountRate from String? to Int, and do the above checks when you init(with json: Dictionary<String,Any>). If the server giving you the dictionary is something you control, have it switch to passing back Ints instead of Strings, and stop dealing with optionals if you don't have to.
Below I have pasted code which you should be able to paste into a Swift 3 playground and see the error.
I have a protocol defined and create an empty array of that type. I then have a class which conforms to the protocol which I try to append to the array but I get the below error.
protocol MyProtocol {
var text: String { get }
}
class MyClass: MyProtocol {
var text = "Hello"
}
var collection = [MyProtocol]()
var myClassCollection = [MyClass(), MyClass()]
collection.append(myClassCollection)
argument type '[MyClass]' does not conform to expected type 'MyProtocol'
Note that collection += myClassCollection returns the following error:
error: cannot convert value of type '[MyProtocol]' to expected argument type 'inout _'
This was working in earlier versions of Swift.
The only solution I have found so far is to iterate and add each element to the new array like so:
for item in myClassCollection {
collection.append(item)
}
Any help appreciated, thanks!
EDIT
The solution as show below is:
collection.append(contentsOf: myClassCollection as [MyProtocol])
The real issue is a misleading compiler error when you are missing "as [MyProtocol]"
The compiler error reads:
error: extraneous argument label 'contentsOf:' in call
collection.append(contentsOf: myClassCollection)
This error causes users to remove contentsOf: from the code which then causes the error I first mentioned.
append(_ newElement: Element) appends a single element.
What you want is append(contentsOf newElements: C).
But you have
to convert the [MyClass] array to [MyProtocol] explicitly:
collection.append(contentsOf: myClassCollection as [MyProtocol])
// or:
collection += myClassCollection as [MyProtocol]
As explained in Type conversion when using protocol in Swift, this
wraps each array element into a box which holds "something that conforms to MyProtocol", it is not just a reinterpretation
of the array.
The compiler does this automatically for a single value (that is why
for item in myClassCollection {
collection.append(item)
}
compiles) but not for an array. In earlier Swift versions, you
could not even cast an entire array with as [MyProtocol], you
had to cast each individual element.
Your trying to append an array when collection is only expecting individual items. For example, changing collection to this compiles:
var collection = [[MyProtocol]]()
here is a way you can go about adding two arrays together:
func merge<T: MyProtocol>(items: inout [T], with otherItems: inout [T]) -> [T] {
return items + otherItems
}
var myClassCollection = [MyClass(), MyClass()]
var myClassCollection2 = [MyClass(), MyClass()]
let combinedArray = merge(items: &myClassCollection, with: &myClassCollection2)
I'm reading through the Swift documentation, looking at the section regarding type casting.
The documentation talks about getting an array of type [AnyObject] from Foundation frameworks stuff (what would be an NSArray * in Objective-C).
First, the documentation provides this example:
for object in someObjects {
let movie = object as Movie
println("Movie: '\(movie.name)', dir. \(movie.director)")
}
Now, I want to change the example slightly, to a case where I don't know all the objects are of type Movie, so I'd do this:
for object in someObject {
if let movie = object as? Movie {
println("Movie: '\(movie.name', dir. \(movie.director)")
}
}
The documentation then provides an example of a better way to write the first loop:
for movie in someObjects as [Movie] {
println("Movie: '\(movie.name)', dir. \(movie.director)")
}
Where we downcast someObjects from an [AnyObject] to a [Movie] so we don't have to downcast within the loop.
And this got me thinking, can the array be option downcast as a whole?
if let someMovies = someObjects as? [Movie] {
for movie in someMovies {
println("Movie: '\(movie.name)', dir. \(movie.director)")
}
}
Does this work? And if so, how bad is this from a performance standpoint? How long would it take to check the type of every object in a 10,000 element array using the optional downcast?
I understand that the implications between this snippet and my previous optional downcast snippet are different. The first will iterate through every object and only attempt to print if the object is a Movie, where the second will only enter the loop if the array can be downcast to a [Movie] array, in which case it will either print all or none, but I can imagine there are situations where this would be preferable.
You've got it -- it works exactly like your example code:
let strings = ["Hi", "Hello", "Aloha"]
let anyObjects: [AnyObject] = strings
if let downcastStrings = anyObjects as? [String] {
println("It's a [String]")
}
// console says "It's a [String]"
No idea about performance, but I wouldn't assume that it will have to iterate through the full array to determine if a downcast is possible.
So I got curious, and ran a quick test with 100,000 simple values in a couple different [AnyObject] configurations, where I'm trying to downcast the array to a [String] vs. downcasting the individual elements:
// var anyObjects: [AnyObject] = [AnyObject]()
// filled with random assortment of Int, String, Double, Bool
Running test with mixed array
downcast array execution time = 0.000522
downcast elements execution time = 0.571749
// var actuallyStrings: [AnyObject] = [AnyObject]()
// filled with String values
Running test with all strings
downcast array execution time = 1.141267
downcast elements execution time = 0.853765
It looks like it's super fast to dismiss the mixed array as non-downcastable, since it just needs to scan until it finds a non-String element. For an array that it can downcast, it clearly has to crunch through the whole array, and takes much longer, although I'm not sure why it's not the same speed as looping through the array and manually checking each element.
Let's try this
var someObjects = [
NSString(),
NSUUID()
]
let uuids = someObjects as? NSUUID[]
uuids is nil
var someOtherObjects = [
NSUUID(),
NSUUID()
]
let all_uuids = someOtherObjects as? NSUUID[]
all_uuids is equal to someOtherObjects
So it looks like it does work. You can use the expression to test if all elements of the array are of the expected type but it will not filter the array to select only the expected type.
How do I convert a JSON array:
Example1:
[{"Name":"John","Surname":"Johnson"},{"Name":"Peter","Surname":"Johnson"}]
into:
Example2:
{"Table1":[{"Name":"John","Surname":"Johnson"}, {"Name":"Peter","Surname":"Johnson"}]}
I have instances where sometimes the values I work with is in an array, and sometimes not (is it just called an object? like in second example? or do I have terminology wrong?) So I need to just add the array into an object(?) in order to be able to use the same function for both Arrays (first example) and Objects with arrays(?)(Example2)
So I need to just add the array into an object(?)
Yes. Also you can check if argument is an array inside your method.
var fn = function(mixed) {
if(Object.prototype.toString.call(mixed) == '[object Array]') {
mixed = {'Table1':mixed}
}
// do stuff
}