SWIFT String? does not have a member named 'element' - arrays

I would like to know how can i fill my label from an Array
func metaDataUpdated(metaData : NSString){
var listItems: NSArray = [metaData.componentsSeparatedByString(";")]
if ([listItems.count] > 0){
println([listItems.objectAtIndex(0)])
titleSong.text = [listItems.objectAtIndex(0)]
}
}
I don't really know how to convert an array to string.

Direct conversion to Swift:
func metaDataUpdated(metaData : String) {
let listItems = metaData.componentsSeparatedByString(";")
if listItems.count > 0 {
print(listItems[0])
titleSong.text = listItems[0]
}
}
Nicer Swift:
func metaDataUpdated(metaData : String) {
let listItems = metaData.componentsSeparatedByString(";")
if let first = listItems.first {
print(first)
titleSong.text = first
}
}
Even nicer Swift, without using Foundation and without the function needing to get every component separated by ";", but only the first one (recommended):
func metaDataUpdated(metaData : String) {
if let index = metaData.characters.indexOf(";") {
let first = metaData[metaData.startIndex ..< index]
print(first)
titleSong.text = first
}
}

you cannot assign NSArray to NSString therefore you need to cast the value of this first index into a string
change this
titleSong.text = [listItems.objectAtIndex(0)]
to
titleSong.text = "\(listItems.objectAtIndex(0))"
or
titleSong.text = listItems[0] as! String
and also change this line to ([listItems.count > 0]) to (listItems.count > 0)
your code will look like this:
Note this not obj-c so remove all []
func metaDataUpdated(metaData : NSString){
var listItems: NSArray = metaData.componentsSeparatedByString(";")
if (listItems.count > 0)
{
println(listItems.objectAtIndex(0))
titleSong.text = listItems.objectAtIndex(0) as! String
}
}

Better use Swift types and objects now: Array instead of NSArray, Dictionary instead of NSDictionary, etc.
func metaDataUpdated(metaData : NSString) {
var listItems = metaData.componentsSeparatedByString(";")
if listItems.count > 0 {
print(listItems[0])
titleSong.text = listItems[0]
}
}
Here componentsSeparatedByString returns an array of strings: [String]. We then use simple index subscripting to retrieve its first value.
Note: I suppose you were trying to adapt code from Objective-C because your example was ridden with [] everywhere...

Put your to string item in "\\()".
For instance:
titleSong.text = "\\([listItems.objectAtIndex(0)])"
Not sure you need the [] brackets though

Related

Swift: filter an array of Element1 [(String, Bool)] to return AND convert to an array of Element2 [String]?

I have an array of [(userID: String, friendBool: Bool)] that I want to filter AND convert to an array of userID's only [String] (removing the friendBool and therefore changing the element). Is there a function in Swift for doing this?
Currently, I am filtering on the array and then using a for loop on the filtered array [(userID: String, friendBool: Bool)] to convert it to an array of [String]. Is there a better way to do this?
Current Code:
let friendArray = [(userID: String, friendBool: Bool)]()
let excludeUsers = [String]()
//Updates with user actions
var userArrayForTableView = [String]()
//Filter friendArray
let newArray = friendArray.filter { (existingFriend) -> Bool in
return !excludeUsers.contains(existingFriend.userID)
}
//Convert array fro [(userID: String, friendBool: Bool)] to [String]
for existingFriend in newArray {
userArrayForTableView.append(existingFriend.userID)
}
What I'm trying to do:
//Loaded on ViewDidLoad
let friendArray = [(userID: String, friendBool: Bool)]()
let excludeUsers = [String]()
//Updates with user actions
var userArrayForTableView = [String]()
//Filter friendArray
/*
Below fitler returns an array of [(userID: String, friendBool: Bool)]...
but I want a filter to return an array of [String] for just userIDs
*/
let newArray = friendArray.filter { (existingFriend) -> Bool in
return !excludeUsers.contains(existingFriend.userID)
}
//ERROR HERE because 'newArray' is not [String]
userArrayForTableView.append(contentsOf: newArray)
What about using compactMap()?
In a certain way, it can be understood as a filter() (that you are already using) + map() (which is the loop for existingFriend in newArray in the first solution)
let userArrayForTableView = friendArray.compactMap({ (existingFriend) in
if excludeUsers.contains($0.userID) {
return nil
} else {
return existingFriend.id
}
})
In a shorter way:
let userArrayForTableView = friendArray.compactMap({ excludeUsers.contains($0.userID) ? nil : $0.userID })

How to convert from a Swift String Set to an Array

I am trying to create an array of words from a string object retrieved from Parse. The object retrieved looks like this:
Then this line of code gives this.
let joinedWords = object["Words"] as! String
How do I convert joinedWords to an Array?
If you don't care about the order, you can use flatMap on the set:
var mySet = Set<String>()
for index in 1...5 {
mySet.insert("testwords\(index)")
}
let myArray = mySet.flatMap { $0 }
print(myArray) // "["testwords5", "testwords3", "testwords4", "testwords2", "testwords1"]"
If you want the list sorted alphabetically, you can make your array a var and use sortInPlace()
var myArray = mySet.flatMap { $0 }
myArray.sortInPlace()
print(myArray) // "["testwords1", "testwords2", "testwords3", "testwords4", "testwords5"]"
If object["Words"] is AnyObject, you will have to unwrap it.
if let joinedWordsSet = object["Words"] as? Set<String> {
var joinedWordsArray = joinedWordsSet.flatMap { $0 }
myArray.sortInPlace()
print(myArray)
}
Swift 3 note: sortInPlace() has been renamed sort().
Many thanks to #JAL for so much time on chat to solve this one. This is what we came up with. Its a bodge and no doubt there is a better way!
When uploading to Parse save the set as an array.
let wordsSet = (wordList?.words?.valueForKey("wordName"))! as! NSSet
let wordsArray = Array(wordsSet)
Then it saves to Parse - looking like a set, not an array or a dictionary.
let parseWordList = PFObject(className: "WordList")
parseWordList.setObject("\(wordsArray)", forKey: "Words")
parseWordList.saveInBackgroundWithBlock { (succeeded, error) -> Void in
if succeeded {
// Do something
} else {
print("Error: \(error) \(error?.userInfo)")
}
}
Then you can drop the [ ] off the string when its downloaded from Parse, and remove the , and add some "" and voila, there is an array that can be used e.g. to add to CoreData.
var joinedWords = object["Words"] as! String
joinedWords = String(joinedWords.characters.dropFirst())
joinedWords = String(joinedWords.characters.dropLast())
let joinedWordsArray = joinedWords.characters.split() {$0 == ","}.map{ String($0) } // Thanks #JAL!

Compare the values in two different arrays

I am trying to compare the values of two arrays in Swift. If a value of array2 is not found in array1 all the array2 found values need to be list and deleted.
I was trying to use the code below but its not working anymore in Swift 2:
let array1 = [["aaa","12"],["bbb","349"],["ccc","91"],["ddd","143"]]
let array2 = ["aaa","SSS","bbb","ccc","QQQ","ZZZ","ddd"]
let notNeededValues = filter(enumerate(zip(array1,array2))) { $1.0 == $1.1 }.map{ $0.0 }
print(notNeededValues)
Not sure if I understand your problem correctly, but the problem seems to be, that your code needs a simple conversion to Swift 2 syntax:
let array1 = [["aaa","12"],["bbb","349"],["ccc","91"],["ddd","143"]]
let array2 = ["aaa","SSS","bbb","ccc","QQQ","ZZZ","ddd"]
let notNeededValues = zip(array1, array2).enumerate().filter { $1.0 == $1.1 }.map { $0.0 }
print(notNeededValues)
Swift is moving away from globally defined functions, like filter and enumerate once were, and is using dot-syntax instead. This change was made possible by protocol extensions, and makes code more readable.
Update:
I assume this is what you mean(?):
let notNeededValues = array2.filter { !array1.map { $0[0] }.contains($0) }
// or like this:
let array1FirstElements = array1.map { $0[0] }
let notNeededValues = array2.filter { !array1FirstElements.contains($0) }
How about this:
let array1 = [["aaa","12"],["bbb","349"],["ccc","91"],["ddd","143"]]
let array2 = ["aaa","SSS","bbb","ccc","QQQ","ZZZ","ddd"]
extension Array where Element: Equatable {
func removeObject(object: Element) -> [Element] {
return filter {$0 != object}
}
}
var filteredArray2 = array2.reduce(array2) {
if array1.flatMap({$0}).contains($1) {
return $0.removeObject($1)
}
return $0
}
print(filteredArray2)

Reduce a string to a dictionary in Swift

What woudl be a simple way to reduce a string like AAA:111;BBB:222;333;444;CCC:555 to a dictionary in Swift. I have the following code:
var str = "AAA:111;BBB:222;333;444;CCC:555"
var astr = str.componentsSeparatedByString(";").map { (element) -> [String:String] in
var elements = element.componentsSeparatedByString(":")
if elements.count < 2 {
elements.insert("N/A", atIndex: 0)
}
return [elements[0]:elements[1]]
}
The code above produces an Array of Dictionaries:
[["A": "111"], ["BBB": "222"], ["UKW": "333"], ["UKW": "444"], ["CCC": "555"]]
I want it to produce
["A": "111", "BBB": "222", "UKW": "333", "UKW": "444", "CCC": "555"]
but no mater what I try, since i call the map function on an Array it seems impossible to convert the nature of the map function's result.
NOTE: The dictionary in string format is described as either having KEY:VALUE; format or VALUE; format, in which case the mapping function will add the "N/A" as being the key of the unnamed value.
Any help on this matter is greatly appreciated.
Your map produces an array of dictionaries. When you want to combine them into 1, that's a perfect job for reduce:
func + <K,V>(lhs: Dictionary<K,V>, rhs: Dictionary<K,V>) -> Dictionary<K,V> {
var result = Dictionary<K,V>()
for (key, value) in lhs {
result[key] = value
}
for (key, value) in rhs {
result[key] = value
}
return result
}
var str = "AAA:111;BBB:222;333;444;CCC:555"
var astr = str
.componentsSeparatedByString(";")
.reduce([String: String]()) {
aggregate, element in
var elements = element.componentsSeparatedByString(":")
if elements.count < 2 {
elements.insert("N/A", atIndex: 0)
}
return aggregate + [elements[0]:elements[1]]
}
print(astr)
Swift has no default operator to "combine" two Dictionaries so you have to define one. Note that the + here is not commutative: dictA + dictB != dictB + dictA. If a key exist in both dictionaries, the value from the second dictionary will be used.
This is a work for reduce:
let str = "AAA:111;BBB:222;333;444;CCC:555"
let keyValueStrings = str.componentsSeparatedByString(";")
let dictionary = keyValueStrings.reduce([String: String]()) {
aggregate, element in
var newAggregate = aggregate
let elements = element.componentsSeparatedByString(":")
let key = elements[0]
// replace nil with the value you want to use if there is no value
let value = (elements.count > 1) ? elements[1] : nil
newAggregate[key] = value
return newAggregate
}
print(dictionary)
You can also make aggregate mutable directly:
let dictionary = keyValueStrings.reduce([String: String]()) {
(var aggregate: [String: String], element: String) -> [String: String] in
let elements = element.componentsSeparatedByString(":")
let key = elements[0]
// replace nil with the value you want to use if there is no value
let value = (elements.count > 1) ? elements[1] : nil
aggregate[key] = value
return aggregate
}
This is a functional approach, but you can achieve the same using a for iteration.
The reason this is happening is because map can only return arrays. If you are using this method to parse your string, then you need to convert it to a dictionary after.
var newDict = [String:String]()
for x in astr {
for (i, j) in x {
newDict[i] = j
}
}
The current issue with your code is that map function iterates over array containing [["key:value"],["key:value"]..] and you separate it again. But it returns ["key":"value"] which you then add to your array.
Instead you can add elements[0]:elements[1] directly to a locally kept variable which will fix your problem. Something like
finalVariable[elements[0]] = elements[1]

How can I tell what the index of an item in a multidimensional array would be if the array were flattened?

I have a multidimensional array with some repeated elements in the deepest level:
[
["taco","burrito"],
["chalupa","taco","bread"]
["pizza","lasagna"],
["pizza","taco","burrito"]
["salad","sandwich"],
["meat","turkey"]
["cups","chicken"],
["rabbit","taco", "chicken", "salad"]
]
I have flattened this array into...
[
"taco",
"burrito",
"chalupa",
"taco",
"bread",
"pizza",
"lasagna",
etc...
]
I have found the second occurrence of the word "taco" in the multidimensional array and know its index.
How can I translate that index to an index in the flattened array?
So in this example it would be...
multiDimensionalArray[0][1][1]
=
flatArray[3]
Swift doesn't have native support for flattening arrays. You could roll this out yourself, or you could use ExSwift which has a flatten method. If you flatten the array successfully, obviously you could then determine which index the value is at using indexOfObject. Since you have multiple identical objects, you may want to use indexesOfObjectsPassingTest, which will give you an index set for all indexes matching the test you provide.
First of all you need a function to flatten the array into an array of strings in order to gain the true indices of the elements.
public func flattenArray(array: Array<AnyObject>) -> Array<String> {
var flattened: Array<String> = []
for object: AnyObject in array {
if object is Array<AnyObject> {
for string in flattenArray(object as Array<AnyObject>) {
flattened.append(string)
}
} else if object is String {
flattened.append(object as String)
}
}
return flattened
}
Then you need to write a function that will obtain the index of an element in an array at a certain number of occurrences
public func indexOfString(string: String, inArray array: Array<String>, occurance: Int = 1) -> Int? {
var occurancesSoFar = 0
var index: Int? = nil
var currentIndex = 0
for object in array {
if object == string {
if (++occurancesSoFar == occurance) {
index = currentIndex
}
}
++currentIndex
}
return index
}
Calling the above functions in order gives the correct index value:
let array = [
["taco","burrito"],
["chalupa","taco","bread"],
["pizza","lasagna"],
["pizza","taco","burrito"],
["salad","sandwich"],
["meat","turkey"],
["cups","chicken"],
["rabbit","taco", "chicken", "salad"]
]
let flat = flattenArray(array)
let index = indexOfString("taco", inArray: flat, occurance: 2)
println(index) // Optional(3)
Using these methods you could encapsulate them into a single function perhaps called
func flattenAndFindElement(element: String,
inArray array: Array<AnyObject>,
atOccurrence occurrence: Int) -> Int?
or something similar.
Hope that helps answer your question.
Here is what I ended up doing:
func findFlattenedIndex(array: Array<Array<String>>, firstIndex: Int, secondIndex: Int) -> Int {
var flatIndex = 0
for (index1,secondTier) in enumerate(array) {
for element in secondTier {
if index1 != firstIndex{
println(element)
flatIndex += 1
}
}
if index1 == firstIndex {
flatIndex += secondIndex
return flatIndex
break
}
}
return flatIndex
}
Of course this could be modified to deal with any number of dimensions.
Thanks for everyone's help with this.

Resources