Check out the latest documentation.

What's Covered

This tutorial illustrates how to use 51Degrees signature entities to derive a subset of signatures which match one or more property : value criteria. The following steps are covered:

  • How to iterate through the signature entities.
  • For each signature entity how to get a list of profiles belonging to the signature.
  • How to compare the two profiles.

Code and Explanation

Example of filtering signatures to obtain a subset based on one or more property : value condition. The example starts with a full set of signatures available in the data file and narrows them down to only leave signatures that correspond to mobile devices running Android OS that use the Chrome browser. The example then prints out a list of device IDs for the remaining signatures and the rank for each signature.

Example covers:

  • Creating a dataset without provider
    											
    											
          Dataset dataset = MemoryFactory.create(
          Shared.getLitePatternV32(), true);
      
    											
    										
  • Converting an Iterable to ArrayList using:
    											
    											
          iterableToArrayList(dataset.getSignatures())
      
    											
    										
  • Using the filterBy method, that:
    1. Performs null checks
    2. Retrieves Property object based on provided property name.
    3. For each signature
      													
      													
                for (Signature sig : listToFilter) {
            
      													
      												


      Gets values for specified property:
      													
      													
                Values vals = sig.getValues(property);
            
      													
      												


      Checks if signature in question contains provided property values:
      													
      													
                if (vals.get(propertyValue) != null)
            
      													
      												
      , and if so, adds the signature to the temporary list:
      													
      													
                filterResults.add(sig);
            
      													
      												
    4. Temporary list is then returned.
  • Accessing deviceId and rank via signature object.

Every invocation of the filterBy() function will narrow down the subset of signatures available based on the provided property name and value. Eventually only a small subset will be left.

Dynamic filtering can be useful in a number of cases. For example: when creating an interdependent menu where one choice narrows down the options in a subsequent choice.



Full Source File
										
    // Dataset that holds device data information.
    protected Dataset dataset;
    // Full list of signatures.
    protected ArrayList<Signature> signatures;
    
    /**
     * Creates a new Dataset using memory factory which loads a 
     * memory-resident representation of data. Also loads a copy of 
     * signatures into an ArrayList.
     * 
     * @throws IOException if there was a problem reading from the data file.
     */
    public DynamicFilters() throws IOException {
        dataset = MemoryFactory.create(Shared.getLitePatternV32(), true);
        signatures = iterableToArrayList(dataset.getSignatures());
    }
    
    /**
     * Instantiates this class and launches the demo.
     * 
     * @param args command line arguments
     * @throws java.io.IOException if there was a problem accessing data file.
     */
    public static void main (String[] args) throws IOException {
        DynamicFilters dfe = new DynamicFilters();
        dfe.run();
    }
    
    /**
     * Repeatedly calls the filter function and prints results. The filtered 
     * subset of signatures represents mobile devices that run on Android OS 
     * and use Chrome Web browser.
     * 
     * @throws IOException if there was a problem accessing data file.
     */
    public void run() throws IOException {
        System.out.println("Total number of signatures in the data file: " + 
                signatures.size());
        
        ArrayList<Signature> subsetOfSignatures = 
                filterBy("IsMobile", "True", null);
        System.out.println("Signatures after removing non-mobile devices: " + 
                subsetOfSignatures.size());
        
        subsetOfSignatures = 
                filterBy("PlatformName", "Android", subsetOfSignatures);
        System.out.println("Signatures after remobing non Android devices: " + 
                subsetOfSignatures.size());
        
        subsetOfSignatures = 
                filterBy("BrowserName", "Chrome", subsetOfSignatures);
        System.out.println("Signatures after removing non Chrome browsers: " + 
                subsetOfSignatures.size());
        
        System.out.println("The following device IDs are for devices that are "
                + "mobile, run on Android OS and use Chrome browser:");
        for (Signature signature : subsetOfSignatures) {
            System.out.println(signature.getDeviceId() + " popularity: " + 
                    signature.getRank());
        }
    }
    
    /**
     * Filters the provided set of signatures to only return those, where the 
     * specified property is equal to the specified value. For example: calling 
     * this function with "IsMobile","True",null will return a subset of 
     * signatures where the IsMobile property evaluates to "True".
     * <p>
     * After checking for valid input method, iterates through the provided list 
     * of signatures to check if the required property of the current signature 
     * has the required value. If so, a signature is added to the temporary list 
     * that gets returned at the end.
     * <p>
     * If the signature list was not provided, a complete list of signatures 
     * available in the data file will be used.
     * 
     * @param propertyName String containing name of the property to check for, 
     * not null.
     * @param propertyValue String with value that the required property must 
     * evaluate to, not null.
     * @param listToFilter an ArrayList of signatures to perform filtering on. 
     * If null the entire set of signatures will be used.
     * @return Subset of signatures where property equals value.
     * @throws IOException if there was a problem accessing data file.
     */
    public ArrayList<Signature> filterBy(String propertyName, 
                                         String propertyValue, 
                                         ArrayList<Signature> listToFilter) 
                                                            throws IOException {
        if (propertyName.isEmpty() || propertyValue.isEmpty()) {
            throw new IllegalArgumentException("Property and Value can not be "
                    + "empty or null.");
        }
        if (listToFilter == null) {
            // Use complete list of signatures if no list was provided.
            listToFilter = signatures;
        }
        Property property = dataset.get(propertyName);
        if (property == null) {
            throw new IllegalArgumentException("Property you requested " +
                    propertyName + " does not appear to exist in the current "
                    + "data file.");
        }
        ArrayList<Signature> filterResults = new ArrayList<Signature>();
        for (Signature sig : listToFilter) {
            Values vals = sig.getValues(property);
            if (vals.get(propertyValue) != null) {
                filterResults.add(sig);
            }
        }
        return filterResults;
    }
    
    /**
     * Converts Iterable list of 51Degrees signatures to an ArrayList of 
     * signatures.
     * 
     * @param <E> generic.
     * @param iter Iterable to convert to ArrayList.
     * @return ArrayList of 51Degrees signatures.
     */
    public static <E> ArrayList<E> iterableToArrayList(Iterable<E> iter) {
        ArrayList<E> list = new ArrayList<E>();
        for (E item : iter) {
            list.add(item);
        }
        return list;
    }
    
    /**
     * Closes the {@link fiftyone.mobile.detection.Dataset} by releasing data 
     * file readers and freeing the data file from locks. This method should 
     * only be used when the {@code Dataset} is no longer required, i.e. when 
     * device detection functionality is no longer required, or the data file 
     * needs to be freed.
     * 
     * @throws IOException if there is a problem accessing the data file.
     */
    public void close() throws IOException {
        dataset.close();
    }


										
Full Source File

Summary

This tutorial covered how to work with the 51Degrees device Data Model to obtain a subset of devices that meet several conditions. Each condition in this case is a specific value for a chosen property. The result should be read as follows: this is a subset of devices where property1 has value1 and property2 has value2 and property3 has value3 and so on.

One real world application for dynamic filtering is building a set of interlinked menus where each choice will narrow down the available options for subsequent choices. This can be useful when part of the API is exposed to the end user:

  • An ad agency could benefit from allowing their clients to target specific devices based on pre-defined criteria, such as DeviceType, ScreenInchesWidth or even PriceBand.
  • A program that uses a 51Degrees API to generate/augment reports could be enhanced to allow the user to choose the report parameters. By p roviding a finite set of choices and avoiding arbitrary input the chance of errors occurring is reduced and user experience improved.

The Lite data file contains considerably fewer properties and values than the Premium or Enterprise files, making the usefulness of this tutorial slightly harder to appreciate. With Premium and Enterprise data files there are many more possibilities for creating subsets of devices. For example: using Premium device data you can generate a subset of all ' Samsung ' ' Smartphone ' devices that run ' Android ' OS version ' 4 ' or list browsers by popularity.