Using functional tools to query data

The std.algorithm module includes a variety of higher-order ranges that provide tools similar to functional tools. We've already used many of them in this chapter. Here, we'll put it all together to see how D code can be similar to a SQL query.

A SQL query is as follows:

SELECT id, name, strcat("Title: ", title)
    FROM users
    WHERE name LIKE 'A%'
    ORDER BY id DESC
    LIMIT 5;

How would we express something similar in D?

Getting ready

Let's create a struct to mimic the data table and make an array with the some demo information. The code is as follows:

struct User {
    int id;
    string name;
    string title;
}
User[] users;
users ~= User(1, "Alice", "President");
users ~= User(2, "Bob", "Manager");
users ~= User(3, "Claire", "Programmer");

How to do it…

Let's use functional tools to query data by executing the following steps:

  1. Import std.algorithm.
  2. Use sort to translate the ORDER BY clause. If your dataset is large, you may wish to sort it at the end. This will likely require a call to an array, but it will only sort the result set instead of everything. With a small dataset, sorting early saves an array allocation.
  3. Use filter to implement the WHERE clause.
  4. Use map to implement the field selection and functions. The std.typecons.tuple module can also be used to return specific fields.
  5. Use std.range.take to implement the LIMIT clause.
  6. Put it all together and print the result.

The code is as follows:

import std.algorithm;
import std.range;
import std.typecons : tuple; // we use this below
auto resultSet = users.
    sort!((a, b) => a.id > b.id). // the ORDER BY clause
    filter!((item) => item.name.startsWith("A")). 
    // the WHERE clause
    take(5).
    map!((item) => tuple(item.id, item.name,"Title: " ~ item.title)); // the field list and transformations
import std.stdio;
foreach(line; resultSet)
    writeln(line[0], " ", line[1], " ", line[2]);

It will print the following output:

1 Alice Title: President

How it works…

Many SQL operations or list comprehensions can be expressed in D using some building blocks from std.algorithm. They all work generally the same way; they take a predicate as a compile-time argument. The predicate is passed one or two items at a time and you perform a check or transformation on it. Chaining together functions with the dot syntax, like we did here, is possible thanks to uniform function call syntax. It could also be rewritten as take(5, filter!pred(map!pred(users))). It depends on author's preference, as both styles work exactly the same way.

It is important to remember that all std.algorithm higher-order ranges are evaluated lazily. This means no computations, such as looping over or printing, are actually performed until they are required. Writing code using filter, take, map, and many other functions is akin to preparing a query. To execute it, you may print or loop the result, or if you want to save it to an array for use later, simply call .array at the end.

There's more…

The std.algorithm module also includes other classic functions, such as reduce. It works the same way as the others.

D has a feature called pure functions, which we'll look at in more depth later in the book. The functions in std.algorithm are conditionally pure, which means they can be used in pure functions if and only if the predicates you pass are also pure. With lambda functions, like we've been using here, the compiler will often automatically deduce this for you. If you use other functions you define as predicates and want to use it in a pure function, be sure to mark them pure as well.

See also

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.181.36