Friday Night Dinner: Don't Tell Dad

Lonsdale Road has many restaurants. They come and go, and we've been to plenty of them in the last few years. But Don't Tell Dad is pretty new.

During the day it's a bakery, but during the evening it turns into a restaurant. It's hip, so booking is a must.

The interior is modern, and full of wood. It wasn't noisy, and it was easy to chat. As our starter, we shared a couple of truffle and cheddar beignets — cheesy balls with shavings of more cheese — and a couple of oxtail crumpets. These were well braised pieces of meat in gravy, upon soft crumpets with some fried crumbs for texture.

We did not fancy ordering a bottle of wine, but instead chose to have a cider with our starter, and a glass of wine each with our main. Unfortunately, the cider only showed up after we finished our starters.

My wife picked monkfish as her main. This was served on the bone with artichoke and a dill béarnaise. I selected the roast lamb, which was served with courgette, chickpeas, and a salsa verde. Both dishes were delicious. However, to be critical whilst the food and wines were lovely, they didn't turn up at the same time. Our wine turned up so far in advance of our main, that we’d nearly finished the glasses of wine when it arrived

Still feeling a little peckish, we also had dessert. Madeleines for my wife, and an olive oil and chocolate mousse for me. Both desserts could easily have been shared between the two of us, and we probably should have. We opted to go for the matching dessert wines, and again there was a significant delay in between the wine and the desserts appearing.

Once they finally arrived, the madeleines were soft, but not so crunchy on the outside. The chocolate mousse was covered in nicely bitter chocolate flakes, and juicy blood orange.

The food at Don't Tell Dad was very nice, as was the atmosphere. Unfortunately, the service was very slow with long waits between the courses. In addition to that, the drinks were served at odd times, and not with the food which they were ordered to go with.

Truffle and Cheddar Beignets
Truffle and Cheddar Beignets
1 / 6
Oxtail Crumpets
Oxtail Crumpets
2 / 6
Roast Lamb
Roast Lamb
3 / 6
Monkfish
Monkfish
4 / 6
Madeleines
Madeleines
5 / 6
Chocolate Mousse
Chocolate Mousse
6 / 6

Shortlink

This article has a short URL available: https://drck.me/dont-tell-dad-jer

Likes

Comments

No comments yet

Friday Night Dinner: Ida

This was our second trip to Ida. We first visited in summer 2023, and although we wrote a review, we never posted it. Ida has achieved something of a reputation in the last couple of years, but remains at its heart a local neighbourhood restaurant.

When you walk past Ida during the day, you wouldn't pay it any notice. It sits on a busy road at the edge of a residential neighbourhood, with a convenience store across the road. It's an unlikely spot for a restaurant, situated pretty close to both Salusbury Road in Queens Park, and the restaurants on Chamberlayne Road close to Kensal Rise. In the daytime it looks slightly unwelcoming, which is probably why it took us so long to visit the first time.

In the evening however, the place livens up. We arrived at Ida at a quarter past six, and we were nearly the first ones in. By the time we were ready to order fifteen minutes later it was full. When we were here last time, on a lovely warm summer evening, there were also a few tables outside. With one set of doors open, even our inside table felt part of the streetscape.

To start, we ordered a crostone to share. The sourdough toast was covered in flavourful (and aromatic) melted taleggio cheese, with grapes and a little syrupy honey.

As his main meal, Derick ordered the wild funghi papperdelle, and as hers, Morag ordered the little ears — orecchiette — which was served with a creamy velouté of cauliflower, and more of the tasty taleggio cheese.

The home-made pasta was nicely al dente, and the sauces thick, slightly sticky, and delicious. We only got (and needed) a fork as utensils, which made the meal more authentic.

With our meal, we also enjoyed their house white wine, which was, fresh with a good flavour that worked well with all our dishes.

After our pasta we were still a bit peckish, and fancied a tiramisu to finish our evening. This was light and fluffy, and we debated whether this was as good as the tiramisu offered by another local restaurant. We think the jury is out on that one though.

Crostone
Crostone
1 / 4
Funghi Papperdelle
Funghi Papperdelle
2 / 4
Orecchiette
Orecchiette
3 / 4
Tiramisu
Tiramisu
4 / 4

Shortlink

This article has a short URL available: https://drck.me/ida-jeg

Likes

Comments

No comments yet

Unicode Collation Sorting

I recently added support to this website to receive Favourites and Replies through the ActivityPub posts that I create from each article.

Replies show up as comments after moderation, and favourites show up as Likes under each of the articles.

When fetching all the likes, my internal API returns the following structure — just like how PDOStatement::fetchAll() returns them:

array(7) {
  [0]=>
  array(8) {
    ["name"]=>
    string(14) "Marcus Bointon"
    ["linked_account"]=>
    string(33) "https://phpc.social/users/Synchro"
  }
  [1]=>
  array(8) {
    ["name"]=>
    string(14) "Derick Rethans"
    ["linked_account"]=>
    string(33) "https://phpc.social/users/derickr"
  }
  [2]=>
  array(8) {
    ["name"]=>
    string(26) "🇵🇸 Álvaro González"
    ["linked_account"]=>
    string(37) "https://mastodon.social/users/kAlvaro"
  }
}

I wanted to sort the likes by name, which turned out harder to be than I had hoped. Mostly because people do odd things with their names sometimes, such as not using an upper-case letter, or by prefixing their names with emojis as you can see in the example above.

If each entry in my array was only a string with a name, and no emoji is prefixed, you can use natcasesort(), but I have both complex objects and an emoji. This means PHP's built-in functions don't cut it.

However, PHP also has the Intl extension, which has a Collator class with a sort method.

To create such a collator in code, use:

$collator = new Collator('root');

The Root Collation is ICU's Default Collation, but as the ICU documentation says:

Not all languages have sorting sequences that correspond with the root collation order because no single sort order can simultaneously encompass the specifics of all the languages. In particular, languages that share a script may sort the same letters differently.

This is good enough for my use case there, where I only want to sort names in a reasonable fashion.

One thing that it does do is to sort lower-case letters before upper-case letters, but this behaviour can be changed by setting CASE_FIRST attribute on the collator to UPPER_FIRST:

$collator->setAttribute(Collator::CASE_FIRST, Collator::UPPER_FIRST);

Letters in non-Latin script will sort after the Latin letters.

However, the Collator->sort() method can only handle arrays of strings, and not complex objects.

To get around this limitation we instead use PHP's built-in usort() function with our own callback. In our callback we can use the Collator::getSortKey() method on each sort element's name array key to create the key that Collator::sort() would have used internally.

For good measure, we also use trim to remove preceding and trailing whitespace. The code to sort the elements now looks like:

$col = new Collator('root');
$col->setAttribute(Collator::CASE_FIRST, Collator::UPPER_FIRST);

usort(
    $allLikes,
    function($a, $b) use ($col) {
        $aName = trim($a['name']);
        $bName = trim($b['name']);
        $aKey = $col->getSortKey($aName);
        $bKey = $col->getSortKey($bName);

        return $aKey <=> $bKey;
    }
);

Now the only problem is that emojis sort before actual letters, so we need to scrub them. Each letter defined in the Unicode standard has properties associated with it. One of those properties is the General Category. If you examine the chart you will fined that for 0061;LATIN SMALL LETTER A the GC is Ll (Letter, Lower-case). The flag emoji from my example is composed of two characters: 1F1F5;REGIONAL INDICATOR SYMBOL LETTER P and 1F1F8;REGIONAL INDICATOR SYMBOL LETTER S. Both of these have as category So (Symbol, Other).

The PCRE library that PHP uses for regular expressions is aware of these categories, and you can select for for them with the \p{…} specifier. The documentation has a page on it.

In our case we want to remove all Symbols (with category S). Changing our trim lines to strip this character class accomplishes this.

When we now combine all of this, we get:

<?php
$allLikes = [
    [ 'name' => 'Marcus Bointon', 'linked_account' => 'https://phpc.social/users/Synchro' ],
    [ 'name' => 'Derick Rethans', 'linked_account' => 'https://phpc.social/users/derickr' ],
    [ 'name' => '🇵🇸 Álvaro González', 'linked_account' => 'https://mastodon.social/users/kAlvaro' ],
];

$col = new Collator('root');
$col->setAttribute(Collator::CASE_FIRST, Collator::UPPER_FIRST);

usort(
    $allLikes,
    function($a, $b) use ($col) {
        $aName = trim(preg_replace('/\p{S}+/u', '', $a['name']));
        $bName = trim(preg_replace('/\p{S}+/u', '', $b['name']));
        $aKey = $col->getSortKey($aName);
        $bKey = $col->getSortKey($bName);

        return $aKey <=> $bKey;
    }
);

var_dump($allLikes);
?>

When we now run the example with the $allLikes array from previously, our result is:

array(3) {
  [0] =>
  array(2) {
    'name' =>
    string(26) "🇵🇸 Álvaro González"
    'linked_account' =>
    string(37) "https://mastodon.social/users/kAlvaro"
  }
  [1] =>
  array(2) {
    'name' =>
    string(14) "Derick Rethans"
    'linked_account' =>
    string(33) "https://phpc.social/users/derickr"
  }
  [2] =>
  array(2) {
    'name' =>
    string(14) "Marcus Bointon"
    'linked_account' =>
    string(33) "https://phpc.social/users/Synchro"
  }
}

And from that, I can then generate the HTML code to render who liked my article, which you can hopefully see below.

Shortlink

This article has a short URL available: https://drck.me/intl-sort-je4

Comments

@blog I've created https://senseexception.github.io/intl-sort/ for such sorting cases.

Become a Patron!
Mastodon
GitHub
LinkedIn
RSS Feed
Flickr
YouTube
Vimeo
Email

My Amazon wishlist can be found here.

Life Line