Check element in array with different objects - ios

I have an array with custom objects by 2 types. Also I have TableView, which shows objects from array. I need to select tableViewCell and check, if the element already in array - remove it from array, otherwise add it to array. I know, there is method for the checking array.contains(element) but my array looks like [Any] and it doesn't have this method.
I'm trying to check it with use for-in, but it's not good solution.
How can I do this?
let a: Int = 5
let b: String = "3"
let array: [Any] = [a, b]

You are able to cast Any to Int or String type and just use array.contains
array.contains {
if let intValue = $0 as? Int {
return intValue == 3
} else if let stringValue = $0 as? String {
return stringValue == "3"
}
return false
}
OR use this extension (Swift 4):
extension Array where Element: Any {
func contains<T: Equatable>(_ element: T) -> Bool {
return contains {
guard let value = $0 as? T else { return false }
return value == element
}
}
}
array.contains("3") // true for your example

Related

Check NSArray contain NSDictionary key in Swift?

I have array of NSDictionary and i want check particular NSDictionary "key" present in NSArray or not.
I tried
if let val = dict["key"] {
if let x = val {
println(x)
} else {
println("value is nil")
}
} else {
println("key is not present in dict")
}
and
let arrayOfKeys = dictionary.allKeys
if (arrayOfKeys.containsObject(yourKey)) {
}
else {
}
but this is for individual array object. Also
if ([[dictionary allKeys] containsObject:key]) {
// contains key
}
This method for individual NSDictionary not for NSArray.
Also
if readArray.contains(["A":1]) { ALToastView.toastInView(UIApplication.sharedApplication().keyWindow, withText: "Already added")
}else{
readArray.append(["A":0]
}
This code will add again same key in array if change value for key "A"
Ex. My array contain dictionary ["A":1] and i want to check key "A" is present or not?
How do i check any key present in Array? Do i need to iterate array? Thanks in advance.
Refer following example :
var array = [[String:Any]]()
array.append(["key1":"value1"])
array.append(["key2":"value2"])
array.append(["key3":"value3"])
array.append(["key4":"value4"])
array.append(["key5":"value5"])
let key = "key5"
if let index = (array.indexOf { (dict) -> Bool in
dict[key] != nil
})
{
print("Key Found at = \(index) ")
} else {
print("Key not Found")
}
You can use this method. Basically you iterate thru the array of dict getting a tuple of its index and dict and if the dict contains the key you store the index in an array.
let arrayOfDict = [
["key1":"value1", "key2":"value2"],
["key3":"value3", "key4":"value4", "key5":"value5"],
["key6":"value6", "key7":"value7", "key8":"value8"],
["key6":"value6", "key7":"value7", "key8":"value8"]
];
let keyToCheck = "key6"
var foundIndex = [Int]()
for (index, dict) in arrayOfDict.enumerate()
{
if let item = dict[keyToCheck] {
foundIndex.append(index)
}
}
if foundIndex.count > 0 {
print("Found at \(foundIndex)")
} else {
print ("not found")
}
You can also use this method if you want to get the dictionaries that contains the keys along with the index.
let newArray = arrayOfDict.enumerate().filter { (tuple:(index: Int, element: Dictionary<String, String>)) -> Bool in
if tuple.element.keys.contains(keyToCheck) {
return true
} else {
return false
}
}
The newArray will be an array of tuples of type (Int:[String:String]) wherein the tuple.0 will be the index and tuple.1 will be the dictionary

Swift filter dictionary error: Cannot assign a value of type '[(_, _)]' to a value of type '[_ : _]'

I am trying to filter a dictionary in swift:
var data: [String: String] = [:]
data = data.filter { $0.1 == "Test" }
the filter code above compiles under Swift 2 but yields the following error:
Cannot assign a value of type '[(String, String)]' to a value of type '[String : String]'
is this a bug in the Swift compiler or is this not the right way to filter dictionaries in Swift?
This has been fixed in Swift 4
let data = ["a": 0, "b": 42]
let filtered = data.filter { $0.value > 10 }
print(filtered) // ["b": 42]
In Swift 4, a filtered dictionary returns a dictionary.
Original answer for Swift 2 and 3
The problem is that data is a dictionary but the result of filter is an array, so the error message says that you can't assign the result of the latter to the former.
You could just create a new variable/constant for your resulting array:
let data: [String: String] = [:]
let filtered = data.filter { $0.1 == "Test" }
Here filtered is an array of tuples: [(String, String)].
Once filtered, you can recreate a new dictionary if this is what you need:
var newData = [String:String]()
for result in filtered {
newData[result.0] = result.1
}
If you decide not to use filter you could mutate your original dictionary or a copy of it:
var data = ["a":"Test", "b":"nope"]
for (key, value) in data {
if value != "Test" {
data.removeValueForKey(key)
}
}
print(data) // ["a": "Test"]
Note: in Swift 3, removeValueForKey has been renamed removeValue(forKey:), so in this example it becomes data.removeValue(forKey: key).
data.forEach { if $1 != "Test" { data[$0] = nil } }
Just another approach (a bit simplified) to filter out objects in your dictionary.
Per Apple docs, filter:
Returns an array containing, in order, the elements of the sequence that satisfy the given predicate.
https://developer.apple.com/reference/swift/sequence/1641239-filter
I found myself needing to do what the OP was asking about and ended up writing the following extensions (Swift 3):
extension Dictionary
{
func filteredDictionary(_ isIncluded: (Key, Value) -> Bool) -> Dictionary<Key, Value>
{
return self.filter(isIncluded).toDictionary(byTransforming: { $0 })
}
}
extension Array
{
func toDictionary<H:Hashable, T>(byTransforming transformer: (Element) -> (H, T)) -> Dictionary<H, T>
{
var result = Dictionary<H,T>()
self.forEach({ element in
let (key,value) = transformer(element)
result[key] = value
})
return result
}
}
Usage:
let data = ["a":"yes", "b":"nope", "c":"oui", "d":"nyet"]
let filtered = data.filteredDictionary({ $0.1 >= "o" })
// filtered will be a dictionary containing ["a": "yes", "c": "oui"]
I've found this method to be useful after filtering or applying some other transform that results in an array of dictionary elements:
extension Array {
func dictionary<K: Hashable, V>() -> [K: V] where Element == Dictionary<K, V>.Element {
var dictionary = [K: V]()
for element in self {
dictionary[element.key] = element.value
}
return dictionary
}
}
To use it, just say something like:
dictionary = dictionary.filter{ $0.key == "test" }.dictionary()
The advantage of this method is that no argument of any kind needs to be passed to the dictionary() method. Generic type arguments tell the compiler everything it needs to know.
You can arrange ascending order according to dictionary value using filter
let arrOfDict = [{"ABC":24},{"XYZ":21},{"AAA":23}]
let orderedDict = arrOfDict.filter{$0.value < $1.value}
you will get below output:
[
{ "XYZ": 21 },
{ "AAA": 23 },
{ "ABC": 24 }
]

How to pass a Type as a parameter in Swift

I have a dictionary of objects and what I would like to do is go through the data set and return an array of objects that conform to a given protocol. I am having issues with the syntax for passing in a desired protocol:
func getObjectsThatConformTo<T>(conformance: T.Type) -> [AnyClass]{
var returnArray: [AnyClass] = []
for(myKey, myValue) in allCreatedObjects{
if let conformantObject = myValue as? conformance{
returnArray.append(conformantObject)
}
return returnArray
}
The error I am seeing is 'conformance' is not a type
Thank you for your help and time
I think this should work:
func getObjectsThatConformToType<T>(type:T.Type) -> [T]{
var returnArray: [T] = []
for(myKey, myValue) in allCreatedObjects{
if let comformantModule = myValue as? T {
returnArray.append(comformantModule)
}
}
return returnArray
}
While you could write a generic-ed method that filters through an array and sees which things in the array are a given type, this problem screams for the use of filter.
Example:
var dict: [String: AnyObject] = [:]
// Populate dict with some values
let strings = dict.values.filter { return $0 is String }
Wrapped in a function that takes type:
func getObjectsThatConformTo<T>(array: [Any], conformance: T.Type) -> [T]? {
return array.filter { return $0 is T } as? [T]
}
Explanation:
Filter is a method on Array which returns a subset of the array based on a test. In this case our test is 'is the element a String?' the filter method accepts a closure with one parameter, the element to be tested, above referred to with $0.
Read up on filter here: https://www.weheartswift.com/higher-order-functions-map-filter-reduce-and-more/

Cast Entry in For in Loop over SequenceType?

Having the following for in loop:
for entry in entries {
println("entry: \(entry)")
}
I want to cast the entry in the loop header to String. I cannot do the following because it is no Array:
for entry in entries as! [String] {
}
because entries is not an array.
Edit: entries conforms the SequenceTypeprotocol.
How can I cast entry in the loop header to String?
There's map for that ;-)
let entries = NSSet(array: ["Foo", "Bar"])
for entry in map(entries, {$0 as! String}) {
println("entry: \(entry)")
}
Hope it helps
The "easy" solution would be
for x in entries {
if let y = x as? String {
println(y)
}
}
but that is probably not what you are looking for.
If it is acceptable that an array of all elements is created first
then the following would work as well:
let a = Array(entries)
for x in a as! [String] {
println(x)
}
If entries is a sequence producing AnyObject elements, and you
know that all these elements are in fact strings, then you can
create a new sequence producing Strings with
let stringSeq = SequenceOf { () -> GeneratorOf<String> in
var gen = entries.generate()
return GeneratorOf {
return gen.next() as? String
}
}
for x in stringSeq {
println(x) // x is a `String`
}
What you probably should do is to change the definition of the
entries generator itself so that it produces strings in the first place. Example (based on the code in your previous
question Make Class iterable with a for in Loop? and this blog post):
// Class containing an array of `AnyObject`:
class MyArrayClass {
var array: [AnyObject] = ["a", "b", "c"]
}
// Sequence producing `String`:
extension MyArrayClass : SequenceType {
func generate() -> GeneratorOf<String> {
var index = 0
return GeneratorOf {
if index < self.array.count {
return self.array[index++] as? String
} else {
return nil
}
}
}
}
// Now the enumeration gives strings:
let entries = MyArrayClass()
for x in entries {
println(x)
}

How do you sort NSMutableArray from JSON in swift?

I made a UITableview with NSMutableArray, having data downloaded from server in form of JSON. When I perform the table cell the code goes like below.
if let rstrntName = self.items[indexPath.row]["rstrnt_name"] as? NSString {
cell.rstrntName.text = rstrntName
}
Now I want to sort it by a column named "rstrnt_name". Below is the code I tried, but it doesn't work.
self.items.sortedArrayUsingComparator({obj1, obj2 -> NSComparisonResult in
let rstrnt1: NSDictionary = obj1 as NSDictionary
let rstrnt2: NSDictionary = obj2 as NSDictionary
if (rstrnt1["rstrnt_name"] as String) < (rstrnt2["rstrnt_name"] as String) {
return NSComparisonResult.OrderedAscending
}
if (rstrnt1["rstrnt_name"] as String) > (rstrnt2["rstrnt_name"] as String) {
return NSComparisonResult.OrderedDescending
}
return NSComparisonResult.OrderedSame
})
How can I sort objects in such type?
Assign the sorted array to anywhere and check -
self.items = self.items.sortedArrayUsingComparator(//rest of your code
should give you the sorted result.
self.items.sortedArrayUsingComparator returns a sorted array which you appear to be throwing away to the ether. Try storing the value instead.
Here is swift Array types sort method with pattern matching. This sort method directly mutates items doesn't return new one.
var items: [[String: AnyObject]] = [["rstrnt_name": "mustafa"], ["rstrnt_name": "Ray"], ["rstrnt_name": "Ali"]]
items.sort { (left, right) -> Bool in
let first = left["rstrnt_name"] as? String
let second = right["rstrnt_name"] as? String
switch (first, second) {
case let (.Some(x), .Some(y)): return x < y
default: return false
}
}

Resources