Many programming languages provide convenient ways to look for a known value in a simple list of strings or numbers.

But what if the elements of the list are themselves compound records/objects/data-structures, and the search condition is more complex than a simple equality test?

Task[edit]
Write a function/method/etc. that can find the first element in a given list matching a given condition.

It should be as generic and reusable as possible.

(Of course if your programming language already provides such a feature, you can use that instead of recreating it.)

Then to demonstrate its functionality, create the data structure specified under #Data set, and perform on it the searches specified under #Test cases.

Data set
The data structure to be used contains the names and populations (in millions) of the 10 largest metropolitan areas in Africa, and looks as follows when represented in JSON:

[
  { "name": "Lagos",                "population": 21.0  },
  { "name": "Cairo",                "population": 15.2  },
  { "name": "Kinshasa-Brazzaville", "population": 11.3  },
  { "name": "Greater Johannesburg", "population":  7.55 },
  { "name": "Mogadishu",            "population":  5.85 },
  { "name": "Khartoum-Omdurman",    "population":  4.98 },
  { "name": "Dar Es Salaam",        "population":  4.7  },
  { "name": "Alexandria",           "population":  4.58 },
  { "name": "Abidjan",              "population":  4.4  },
  { "name": "Casablanca",           "population":  3.98 }
]
However, you shouldn't parse it from JSON, but rather represent it natively in your programming language.

 The top-level data structure should be an ordered collection (i.e. a list, array, vector, or similar).
 Each element in this list should be an associative collection that maps from keys to values (i.e. a struct, object, hash map, dictionary, or similar).
 Each of them has two entries: One string value with key "name", and one numeric value with key "population".
 You may rely on the list being sorted by population count, as long as you explain this to readers.


If any of that is impossible or unreasonable in your programming language, then feel free to deviate, as long as you explain your reasons in a comment above your solution.

Test cases


 Search

 Expected result


 Find the (zero-based) index of the first city in the list whose name is "Dar Es Salaam"

 6


 Find the name of the first city in this list whose population is less than 5 million

 Khartoum-Omdurman


 Find the population of the first city in this list whose name starts with the letter "A"

 4.58

Guidance
If your programming language supports higher-order programming, then the most elegant way to implement the requested functionality in a generic and reusable way, might be to write a function (maybe called "find_index" or similar), that takes two arguments:

 The list to search through.
 A function/lambda/closure (the so-called "predicate"), which will be applied in turn to each element in the list, and whose boolean return value defines whether that element matches the search requirement.
If this is not the approach which would be most natural or idiomatic in your language, explain why, and show what is.

Related tasks
 Search a list

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
 
/**
 * Represent a City and it's population.
 * City-Objects do have a natural ordering, they are ordered by their poulation (descending)
 */
class City implements Comparable<City> {
    private final String name;
    private final double population;
 
    City(String name, double population) {
        this.name = name;
        this.population = population;
    }
 
    public String getName() {
        return this.name;
    }
 
    public double getPopulation() {
        return this.population;
    }
 
    @Override
    public int compareTo(City o) {
        //compare for descending order. for ascending order, swap o and this
        return Double.compare(o.population, this.population);
    }
}
 
public class SearchListOfRecords {
 
    public static void main(String[] args) {
 
        //Array-of-City-Objects-Literal
        City[] datasetArray = {new City("Lagos", 21.),
                new City("Cairo", 15.2),
                new City("Kinshasa-Brazzaville", 11.3),
                new City("Greater Johannesburg", 7.55),
                new City("Mogadishu", 5.85),
                new City("Khartoum-Omdurman", 4.98),
                new City("Dar Es Salaam", 4.7),
                new City("Alexandria", 4.58),
                new City("Abidjan", 4.4),
                new City("Casablanca", 3.98)};
 
        //Since this is about "collections smarter that arrays", the Array is converted to a List
        List<City> dataset = Arrays.asList(datasetArray);
 
        //the City-Objects know that they are supposed to be compared by population
        Collections.sort(dataset);
 
 
        //Find the first City that matches the given predicate and print it's index in the dataset
        //the Predicate here is given in the form a Java 8 Lambda that returns true if the given name
        //Note that the Predicate is not limited to searching for names. It can operate on anything one can done with
        // and compared about City-Objects
        System.out.println(findIndexByPredicate(dataset, city -> city.getName().equals("Dar Es Salaam")));
 
        //Find the first City whose population matches the given Predicate (here: population <= 5.) and print it's name
        //here the value is returned an printed by the caller
        System.out.println(findFirstCityByPredicate(dataset, city -> city.getPopulation() <= 5.));
 
        //Find the first City that matches the given predicate (here: name starts with "A") and
        //apply the given consumer (here: print the city's population)
        //here the caller specifies what to do with the object. This is the most generic solution and could also be used to solve Task 2
        applyConsumerByPredicate(dataset, city -> city.getName().startsWith("A"), city -> System.out.println(city.getPopulation()));
 
    }
 
    /**
     * Finds a City by Predicate.
     * The predicate can be anything that can be done or compared about a City-Object.
     * <p>
     * Since the task was to "find the index" it is not possible to use Java 8's stream facilities to solve this.
     * The Predicate is used very explicitly here - this is unusual.
     *
     * @param dataset the data to operate on, assumed to be sorted
     * @param p       the Predicate that wraps the search term.
     * @return the index of the City in the dataset
     */
    public static int findIndexByPredicate(List<City> dataset, Predicate<City> p) {
        for (int i = 0; i < dataset.size(); i++) {
            if (p.test(dataset.get(i)))
                return i;
        }
        return -1;
    }
 
    /**
     * Finds and returns the name of the first City where the population matches the Population-Predicate.
     * This solutions makes use of Java 8's stream facilities.
     *
     * @param dataset   the data to operate on, assumed to be sorted
     * @param predicate a predicate that specifies the city searched. Can be "any predicate that can be applied to a City"
     * @return the name of the first City in the dataset whose population matches the predicate
     */
    private static String findFirstCityByPredicate(List<City> dataset, Predicate<City> predicate) {
        //turn the List into a Java 8 stream, so it can used in stream-operations
        //filter() by the specified predicate (to the right of this operation, only elements matching the predicate are left in the stream)
        //find the first element (which is "the first city..." from the task)
        //get() the actualy object (this is necessary because it is wrapped in a Java 8 Optional<T>
        //getName() the name and return it.
        return dataset.stream().filter(predicate).findFirst().get().getName();
    }
 
    /**
     * In specified dataset, find the first City whose name matches the specified predicate, and apply the specified consumer
     * <p>
     * Since this can be solved pretty much like the "find a city by population", this has been varied. The caller specifies what to do with the result.
     * So this method does not return anything, but requiers a "consumer" that processes the result.
     *
     * @param dataset      the data to operate on, assumed to be sorted
     * @param predicate    a predicate that specifies the city searched. Can be "any predicate that can be applied to a City"
     * @param doWithResult a Consumer that specified what to do with the results
     */
    private static void applyConsumerByPredicate(List<City> dataset, Predicate<City> predicate, Consumer<City> doWithResult) {
        //turn the List in to a Java 8 stream in stream-operations
        //filter() by the specified predicate (to the right of this operation, only elements matching the predicate are left in the stream)
        //find the first element (which is "the first city..." from the task)
        // if there is an element found, feed it to the Consumer
        dataset.stream().filter(predicate).findFirst().ifPresent(doWithResult);
    }
}
 