Check if array contains something - Swift 4 - arrays

I've found the .contains(Element) method pretty essential in my minimal experience writing Swift code, and have quickly realized that Apple changed it...
func contains(check:[[[Int]]], forElement: [[Int]]) -> Bool {
for element in check {
if areEqual(element, forElement) {
return true
}
}
return false
}
func areEqual(_ a:[[Int]], _ b:[[Int]]) -> Bool {
for i in 0..<a.count {
if a[i] != b[i] {
return false
}
}
return true
}
I've been messing with some large arrays, so I fixed my problem with that clunky function.
What happened?
How do you use the new way?
The example there is well over my head.
enum HTTPResponse {
case ok
case error(Int)
}
let lastThreeResponses: [HTTPResponse] = [.ok, .ok, .error(404)]
let hadError = lastThreeResponses.contains { element in
if case .error = element {
return true
} else {
return false
}
}
// 'hadError' == true

How about using this
let numbers = [1, 2, 3, 4]
let contains = numbers.contains(where: { $0 == 3 })
//contains == true
Or
let strings = ["A", "B", "C", "D"]
let contains = strings.contains(where: { $0 == "B" })
//contains == true
Even with other objects like NSColor
let colors: [NSColor] = [.red, .blue, .green]
contains = colors.contains(where: { $0 == .red })
//contains == true

That API just lets you pass a block where you can perform any checks you want. You can check whether the given element is equal to something, or whether its properties satisfy some constraints (such as in the example in the documentation). You should be able to perform a basic equality check like this:
func contains(check:[[[Int]]], forElement: [[Int]]) -> Bool {
return check.contains { element in
return areEqual(element, forElement)
}
}
func areEqual(_ a:[[Int]], _ b:[[Int]]) -> Bool {
for i in 0..<a.count {
if a[i] != b[i] {
return false
}
}
return true
}
The contains block iterates over every element and returns either true or false. In the end, a single true for any element will return true for the whole function.

Related

Swift array of dictionary , how to check any one element has greater than "one" value

var abc: [[String: Int ]] =
[["a", 0, "b": 0, "c" : 1]]
In the above example, I need a logic where I will check whether the array of dictionary has any element(value) which is greater than 1.
In my project I am navigating user to the next screen, if the array of dictionary contains at least one element which is greater than 1.
There is a typo in your code example, but you can find the value with
func ifContains(arr: [[String: Int ]], value: Int) -> Bool {
for dict in arr {
return dict.values.filter { $0 > 1 }.count > 0
}
return false
}
print(ifContains(arr: abc, value: 1))
This should do the trick:
let containsNonZeroValue = array.contains(where: { aSubDict in
return aSubDict.contains { (key: String, value: Int) -> Bool in
return value > 1
}
})
We check the different aSubDict if they contains a dictionary value superior to 1.
We could have used filter(), map(), flatMap(), etc, but contains() has the advantage of stopping once a value has been found. So if you have a big array, and 2 at the 1 index, it won't iterate over the rest of the array.
In a more concatenated way if you know how to use closures and their arguments:
let containsNonZeroValue = array.contains(where: { $0.contains { $0.value > 1 } })
As suggested by #Leo Dabus in the comments, instead of iterating all the values of the sub dictionary, we can get all the values (aSubDict.values), and check if one of them respects the constraint.
let containsNonZeroValue = array.contains(where: { aSubDict in
return aSubDict.values.contains(where: { $0 > 1 })
})
In the shorter way:
let containsNonZeroValue = array.contains(where: { $0.values.contains(where: { $0 > 1 }) })

How to get a tuple from two lists of elements

I have two lists of UIViews, some of that UIViews have an accessibilityIdentifier most of them are nil.
I'm searching for a way to generate a tuple(or something) with the two UIViews from the lists that have the same accessibilityIdentifier.
The lists are not sorted or something.
Is there a way to not iterate multiple times through the second list to find every pair?
for view in firstViewList {
if view.accessibilityIdentifier != nil {
for secondView in secondViewList {
if secondView.accessibilityIdentifier != nil && secondView.accessibilityIdentifier == view.accessibilityIdentifier {
viewPairs.append((firstView: view, secondView: secondView))
}
}
}
}
I think this is not very efficient.
Make a dict that indexes both view lists by their ID, filter out the ones where the ID is nil, and then use the keys common to both dicts to create a new dict that indexes pairs of same-id views.
Here's a rough example (which I haven't compiled myself).
func makeDictByAccessibilityID(_ views: [UIView]) -> [AccessibilityID: UIView] {
return Dictionary(uniqueKeysWithValues:
firstViewList
.lazy
.map { (id: $0.accessibilityIdentifier, view: $0) }
.filter { $0.id != nil }
)
}
viewsByAccessibilityID1 = makeDictByAccessibilityID(firstViewList)
viewsByAccessibilityID2 = makeDictByAccessibilityID(secondViewList)
commonIDs = Set(viewsByAccessibilityID1.keys).intersecting(
Set(viewsByAccessibilityID2.keys)
)
let viewPairsByAccessibilityID = Dictionary(uniqueKeysWithValues:
commonIDs.lazy.map { id in
// Justified force unwrap, because we specifically defined the keys as being available in both dicts.
(key: id, viewPair: (viewsByAccessibilityID1[id]!, viewsByAccessibilityID2[id]!))
}
}
This runs in O(n) time, which is the best you can get for this problem.
I think you should first filtered your two arrays from the nil value then you can do like this
let tempfirstViewList = firstViewList.filter { (view) -> Bool in
view.accessibilityIdentifier != nil
}
var tempsecondViewList = secondViewList.filter { (view) -> Bool in
view.accessibilityIdentifier != nil
}
tempfirstViewList.forEach { (view) in
let filterdSecondViewList = tempsecondViewList.filter { (secondView) -> Bool in
secondView.accessibilityIdentifier == view.accessibilityIdentifier
}
if let sView = filterdSecondViewList.first {
viewPairs.append((firstView: view, secondView: sView))
//after add it to your tuple remove it from the temp array to not loop throw it again
if let index = tempsecondViewList.firstIndex(of: sView) {
tempsecondViewList.remove(at: index)
}
}
}
This solution creates an array of tuples with views from the first and second list respectively
var viewArray: [(UIView, UIView)]
firstViewList.forEach( { view in
if let identifier = view.accessibilityIdentifier {
let arr = secondViewList.filter( {$0.accessibilityIdentifier == identifier} )
arr.forEach( { viewArray.append((view, $0))})
}
})

sort an array with Custom Objects swift 4

public class OfferObjectModel: JSONJoy {
let rank : Int?
}
offersList.sort(by: { $0.rank < $1.rank }) -->Binary operator '<' cannot be applied to two 'Int?' operands
offersList is an array of OfferObjectModel
You can do something like this:
a.sort { (first, second) -> Bool in
guard let firstRank = first.rank else { return true }
guard let secondRank = second.rank else { return false }
return firstRank < secondRank
}
So this code block will make sure your nil ranks will be first and then sorted by rank value. And if change this part:
guard let firstRank = first.rank else { return false }
guard let secondRank = second.rank else { return true }
Nil ranks will be at the end.
And please make sure that your array is mutable.

Get shorter version of for loop?

I'm using Swift 3.0 and have this code for searching an item in an array as an extension of String type:
extension String {
func equal(compareToArray : [String]) -> Bool {
for s in compareToArray {
if self == s {
return true
}
}
return false
}
}
It runs fine, but my question is, can I do it better (shorter/more simple or faster)?
Okay, another similar sample:
func contains(compareToArray : [String]) -> Bool {
for s in compareToArray {
if self.contains(s) {
return true
}
}
return false
}
Shorter, simpler, faster
let compareToArray = ["foo", "bar", "baz"]
compareToArray.contains("bar")
Edit:
According to your second example
!compareToArray.filter{ $0.contains("oo") }.isEmpty
compareToArray.index(where: {$0.contains("oo")}) != nil
If you want to check whether an element belong to an Array, in Swift 3.0 this is a better way:
use :
array.index(of: element) -> Int?
Ex:
let myArray = ["a", "b", "c"]
let needle = "b"
if let pos = myArray.index(of: needle') {
print("\(needle) is in array at position : \(pos)"
} else {
print("It's not in array")
}

How to create a predicate to filter array of enums with associated values in Swift?

enum EnumType {
case WithString(String)
}
var enums = [EnumType]()
enums.append(EnumType.WithString("A"))
enums.append(EnumType.WithString("B"))
enums.append(EnumType.WithString("C"))
enums.append(EnumType.WithString("D"))
enums.append(EnumType.WithString("E"))
enums.append(EnumType.WithString("F"))
How to filter my enums array to find the one with associated value equal C. What predicate do I need to use?
The filter function can either be invoked as a global function over an array, or as an instance method (I prefer the later as it is more OO).
It accepts a closure with one parameter (the element being evaluated) that return a boolean (indicating whether the element matches the required condition).
Since it's a simple closure in a clear situation it's ok to use the abbreviated form.
I guess that other "With" cases would be added to your enum, so you could go with something like:
let filteredArray = enums.filter {
switch $0 {
case let .WithString(value):
return value == "C"
default:
return false
}
}
That should do the trick in your example.
As someone already mentioned, for Swift > 2.0 there's if case statement available:
enums.filter {
if case .WithString("C") = $0 {
return true
}
return false
}
But, it doesn't look nice, especially if you are going to repeat same logic again. What we can do here is to make EnumType conform to Equatable:
extension EnumType: Equatable {
}
func ==(lhs: EnumType, rhs: EnumType) -> Bool {
switch (lhs, rhs) {
case (.WithString(let lStr), .WithString(let rStr)):
return lStr == rStr
}
}
And now you can just:
enums.filter { $0 == .WithString("C") }
You could try adding a simple extension with a computed property to your enum and filter to that property:
extension EnumType {
var isC: Bool {
switch self {
case .WithString(let message): return message == "C"
default: return false
}
}
}
After this, you could simpy use the filtering as usual:
enums.filter { $0.isC }
var filteredArray = enums.filter { element in
switch element {
case EnumType.WithString(let string):
return string == "A"
default:
return false
}
}
This can probably be simplified with Swift 2.0 binding of assosciated values in if statements.
Inspired by #Jessy and SwiftLee, here is my solution:
// -----------------------
// CaseReflectable
// -----------------------
// designed for enums only
// (use it on other types not recommended)
protocol CaseReflectable {}
// default behaviors.
extension CaseReflectable {
/// case name
var caseName: String {
let mirror = Mirror(reflecting: self)
// enum cases:
// - normal case: no children
// - case with associated values: one child (with label)
guard let label = mirror.children.first?.label else {
return "\(self)" // normal case
}
// case with associated values
return label
}
/// associated values
var associatedValues: Any? {
// if no children, a normal case, no associated values.
guard let firstChild = Mirror(reflecting: self).children.first else {
return nil
}
return firstChild.value
}
}
// --------------------------
// custom operator ~=
// --------------------------
/// match enum cases with associated values, while disregarding the values themselves.
/// usage: `Enum.enumCase ~= instance`
func ~= <Enum: CaseReflectable, AssociatedValue>(
// an enum case (with associated values)
enumCase: (AssociatedValue) -> Enum, // enum case as function
// an instance of Enum
instance: Enum
) -> Bool
{
// if no associated values, `instance` can't be of `enumCase`
guard let values = instance.associatedValues else { return false }
// if associated values not of the same type, return false
guard values is AssociatedValue else { return false }
// create an instance from `enumCase` (as function)
let case2 = enumCase(values as! AssociatedValue)
// if same case name, return true
return case2.caseName == instance.caseName
}
// ------------
// Enum
// ------------
// enum with associated values
// (conforms to `CaseReflectable`)
enum Enum: CaseReflectable {
case int(Int)
case int2(Int)
case person(name: String, age: Int)
case str(String)
}
// ------------
// main
// ------------
let a: Enum = .int(3)
Enum.int ~= a // true
Enum.int2 ~= a // false
let joe = Enum.person(name: "joe", age: 8)
Enum.person ~= joe // true
Enum.int ~= joe // false
// array of enum cases
let items: [Enum] = [
.int(1), .str("hi"), .int(2)
]
// filter enum cases
let filtered = items.filter { Enum.int ~= $0 }
print(filtered) // [Enum.int(1), Enum.int(2)]
You can achieve something more reusable by implementing Equatable protocol:
enum EnumType {
case WithString(String)
}
extension EnumType: Equatable {
static func ==(lhs: EnumType, rhs: String) -> Bool {
switch lhs {
case .WithString(let value):
return value == rhs
}
}
}
EnumType.WithString("F") == "A" // false
EnumType.WithString("F") == "F" // true

Resources