Showing posts with label contains. Show all posts
Showing posts with label contains. Show all posts

Wednesday, August 8, 2018

Swift Equatable Protocol


In this tutorial, let’s see what is like confirming to Equatable protocol and where exactly it fits in app development.


It’s quite often we compare things/objects.

       
        let currentAddress = "New York, America"
        let selectedAddress = "New York, America"
        
        if currentAddress == selectedAddress {
            print("Deliver to current address.")
        } else {
            print("Deliver to selected address.")
        }




As these are strings, directly we can use equal to(==) operator.

What If thy are custom objects ?

       
  let currentAddress = Address(city: "Amsterdam", 
                                              state: "New York", 
                                              country: "America")

  let selectedAddress = Address(city: "Amsterdam", 
                                                state: "New York", 
                                                country: "America")
        
  if currentAddress == selectedAddress { 
            
  }




If you compare two custom objects, We get below build error,

// Binary operator '==' cannot be applied to two 'Address' operands

Here what we can do is to write a method in Address struct.

       
 struct Address {
    
    let city: String
    let state: String
    let country: String
    
    func isEqual(to address: Address) -> Bool {
        if city == address.city && 
           state == address.state && 
           country == address.country {

            return true
        }
        return false
    }
    
 }

  let currentAddress = Address(city: "Amsterdam", 
                                              state: "New York", 
                                              country: "America")

  let selectedAddress = Address(city: "Amsterdam", 
                                                state: "New York", 
                                                country: "America")
        
   if selectedAddress.isEqual(to: currentAddress) {
            
   }




This solves the scenario for now.

What if we have an array of addresses and we need to find a address in that?

And we can not directly use contains by passing element like below.

       
  var countries = ["Canada", "Australia", "India", "America"]


   let country = "India"
        
   if countries.contains(country) {
            
   }



We have to use the Array’s contains(where) closure.




       
  let addresses = [
                             Address(city: "Amsterdam", state: "New York", country: "America"), 
                             Address(city: "Belmont", state: "California", country: "America"),  

                             Address(city: "Hartford", state: "Connecticut", country: "America")
                          ]
        
  let selectedAddress = Address(city: "Hartford", state: "Connecticut", country: "America")
        
        let contains = addresses.contains { (address) -> Bool in

            return (selectedAddress.city == address.city &&
                selectedAddress.state == address.state &&
                selectedAddress.country == address.country)
        }



We need to check with the Array contains method with the same condition we used in Address struct method isEqual()


       
     func isEqual(to address: Address) -> Bool {
        if city == address.city && 
           state == address.state && 
           country == address.country {

            return true
        }
        return false
    }

    let contains = addresses.contains { (address) -> Bool in

            return (selectedAddress.city == address.city &&
                selectedAddress.state == address.state &&
                selectedAddress.country == address.country)
     }




Isn’t it a tedious job?


So, This is the use case of Equatable protocol, where it gives the easy solution/way for comparing two objects and finding an object in a collection.

It is as simple as shown below.

Let our Address structure confirm to Equatable protocol.

Earlier, prior to Swift 4.1 version, we need to implement below == static method where we can write equality logic.

       
 struct Address: Equatable {
    
    let city: String
    let state: String
    let country: String
    
    public static func == (lhs: Address, rhs: Address) -> Bool {
        return lhs.city == rhs.city &&
        lhs.state == rhs.state &&
        lhs.country == rhs.country
    }
    
 }




From Swift 4.1 onwards, we don’t need to write the boilerplate code for Equatable protocol unless we need to change the logic.

       
 struct Address: Equatable {
    
    let city: String
    let state: String
    let country: String    
    
 }



By default, boilerplate code compares all the stored properties.

       
 public static func == (lhs: Address, rhs: Address) -> Bool {


        return lhs.city == rhs.city &&
                  lhs.state == rhs.state &&
                  lhs.country == rhs.country
  }



If you want to customise you can implement the == method. In this case, If you want to compare just city and state.

       
 struct Address: Equatable {
    
    let city: String
    let state: String
    let country: String
    
    public static func == (lhs: Address, rhs: Address) -> Bool {

        return lhs.state == rhs.state &&
                  lhs.country == rhs.country
    }
    
 }



Now, we can compare objects like native things.


       
    let currentAddress = Address(city: "Amsterdam", 
                state: "New York", country: "America")


    let selectedAddress = Address(city: "Amsterdam", 
                state: "New York", country: "America")

    if currentAddress == selectedAddress {
            
    }




And also below. When we call !=, It takes the reverse of == method. No need to write extra code for !=
        
       
   if currentAddress != selectedAddress {
            
    }




So, comparing objects became easy with Equatable protocol. Let’s see finding an element in a collection if all the objects in the collection confirm to Equatable protocol.

       
 let addresses = [

          Address(city: "Amsterdam", state: "New York", country: "America"), 


          Address(city: "Belmont", state: "California", country: "America"), 


          Address(city: "Hartford", state: "Connecticut", country: "America")


  ]


  let selectedAddress = Address(city: "Hartford", state: "Connecticut", country: "America")




I want to check whether addresses collection contains selected address or not. As Address confirm to Equatable protocol and addresses array is a collection of Address objects. We get another useful and simple contains() method now.





             
 let addresses = [

          Address(city: "Amsterdam", state: 

                 "New York", country: "America"), 

          Address(city: "Belmont", state: 

                 "California", country: "America"), 

          Address(city: "Hartford", state: 

               "Connecticut", country: "America")

  ]


  let selectedAddress = Address(city: 

                            "Hartford", state: 
               "Connecticut", country: "America")


  if addresses.contains(selectedAddress) {
            
  }




We can simply find objects now using contains.

That’s how Equatable makes it easy of comparing custom objects and finding custom objects in a collection.

Hope this post is useful. Feel free to comment in case of any queries.



Friday, July 1, 2016

Objective C NSPredicate Part 3/4

In the first part, We have seen some Basic comparisons and in the second part, We have seen String comparisons. In this part let's see some compound comparisons.

Let's take an array of users which may come as a JSON service response. I am hardcoding the stuff here.


   
 NSMutableArray *arrUsers = [[NSMutableArray allocinit];
    
    for(int i=0;i<=5;i++){
        
        NSMutableDictionary *dict = [[NSMutableDictionary allocinit];
        
        switch (i) {
                
            case 0:

                [dict setObject:@"John" forKey:@"name"];
                [dict setObject:@"Programmer" forKey:@"designation"];
                [dict setObject:[NSNumber numberWithInt:10000forKey:@"salary"];
                break;
                
            case 1:

                [dict setObject:@"Smith" forKey:@"name"];
                [dict setObject:@"Senior Manager" forKey:@"designation"];
                [dict setObject:[NSNumber numberWithInt:35000forKey:@"salary"];
                break;
                
            case 2:
                [dict setObject:@"Tim" forKey:@"name"];
                [dict setObject:@"Manager" forKey:@"designation"];
                [dict setObject:[NSNumber numberWithInt:30000forKey:@"salary"];
                break;
                
            case 3:

                [dict setObject:@"James" forKey:@"name"];
                [dict setObject:@"Senior Manager" forKey:@"designation"];
                [dict setObject:[NSNumber numberWithInt:32000forKey:@"salary"];
                break;
                
            case 4:

                [dict setObject:@"George" forKey:@"name"];
                [dict setObject:@"Manager" forKey:@"designation"];
                [dict setObject:[NSNumber numberWithInt:28000forKey:@"salary"];
                break;
                
            case 5:

                [dict setObject:@"Martin" forKey:@"name"];
                [dict setObject:@"Manager" forKey:@"designation"];
                [dict setObject:[NSNumber numberWithInt:26000forKey:@"salary"];
                break;
                
            default:
                break;
        }
        
        
        [arrUsers addObject:dict];
        
    }


Now we have a list of all users and we have their name, designation and salary. We may need to filter those users for display purpose based on their name/designation/salary. Let's see some example scenarios and the predicates which works for that filtration.

#1 All the users having salary greater than 30000 and designation is Manager and above


   
 NSPredicate *predicate = [NSPredicate predicateWithFormat:
                                              @"(%K > %d) AND (%K contains[c] %@)",@"salary",    
            [[NSNumber numberWithInt:30000intValue],@"designation",@"manager"];

    
    NSArray *filteredArr = [arrUsers filteredArrayUsingPredicate:predicate];
    
    NSLog(@"Filtered Arr : %@",filteredArr);


  filteredArr : (
        {
        designation = "Senior Manager";
        name = Smith;
        salary = 35000;
    },
        {
        designation = "Senior Manager";
        name = James;
        salary = 32000;
    }
  )



In the predicate, %K is the placeholder for the key values (salary, designation...).

The same predicate can be split into 2 separate predicates and could be applied on the list as shown below.





   
 NSPredicate *onSalary = [NSPredicate predicateWithFormat:
               @"%K > %d",@"salary",[[NSNumber numberWithInt:30000intValue]];

    NSPredicate *onDesignation = [NSPredicate predicateWithFormat:
                                              @"%K contains[c] %@",@"designation",@"manager"];
    
    NSPredicate *predicate = [NSCompoundPredicate 
                              andPredicateWithSubpredicates:@[onSalary, onDesignation]];                                                                                  
    
    NSArray *filteredArr = [arrUsers filteredArrayUsingPredicate:predicate];
    
    NSLog(@"filteredArr : %@",filteredArr);

  filteredArr : (
        {
        designation = "Senior Manager";
        name = Smith;
        salary = 35000;
    },
        {
        designation = "Senior Manager";
        name = James;
        salary = 32000;
    }
  )



#2 All the users having Manager and above as designation and name containing either 'ti' or 'it'


   
 NSPredicate *predicate = [NSPredicate predicateWithFormat:
                               @"(%K contains[c] %@) AND ((name contains[c] %@) OR 
                 (name contains[c] %@))",@"designation",@"manager",@"ti",@"it"];
    
    NSArray *filteredArr = [arrUsers filteredArrayUsingPredicate:predicate];
    
    NSLog(@"filteredArr : %@",filteredArr);

  filteredArr : (
        {
        designation = "Senior Manager";
        name = Smith;
        salary = 35000;
    },
        {
        designation = Manager;
        name = Tim;
        salary = 30000;
    },
        {
        designation = Manager;
        name = Martin;
        salary = 26000;
    }
  )


Using AND, I have added a sub query here with an OR condition for name.


#3 Among some users, I need to check who and all are having salary > 30000



   
 NSPredicate *predicate = [NSPredicate predicateWithFormat:
                                     @"(name in {%@, %@, %@}) AND (salary > %d)",
                                                      @"Tim",@"Martin",@"James",@"Smith",
                                          [[NSNumber numberWithInt:30000intValue]];
    
    NSArray *filteredArr = [arrUsers filteredArrayUsingPredicate:predicate];
    
    NSLog(@"filteredArr : %@",filteredArr);

  filteredArr : (
        {
        designation = "Senior Manager";
        name = Smith;
        salary = 35000;
    },
        {
        designation = "Senior Manager";
        name = James;
        salary = 32000;
    }
  )


I am checking among {Tim, Martin, James, Smith}, whose salary is greater than 30000. For that I am using IN operation, which filters users having the names in that list.

In the next part, let's discuss Relational operations.

Hope this post is useful. Feel free to comment in case of any queries.