Article Series

This article series discuss more than 30 different programming languages. Please read overview before you read any of the details.

Playing with Records Related Articles.

Where to Discuss?

Local Group

Preface

Goal: Learning PHP without HTML burden.

A practical case to collect unique record fields using PHP.

You can use PHP in CLI as well.

Reference Reading

The last time I read php documentation seriously, was in 2011 (a decade ago). There have been some changes, and interesting things.

Source Examples

You can obtain source examples here:


Common Use Case

Task: Get the unique tag string

Please read overview for more detail.

Prepopulated Data

Songs and Poetry

We can use PHP class.

<?php
class MySong {
  public function __construct(
    public String $title = "",
    public Array  $tags  = []
  ) {}
}

$songs = [
  new MySong(
    'Cantaloupe Island', ['60s', 'jazz']),
  new MySong('Let It Be', ['60s', 'rock']),
  new MySong(
    'Knockin\' on Heaven\'s Door',
    ['70s', 'rock']),
  new MySong('Emotion', ['70s', 'pop']),
  new MySong('The River'),
];

__

PHP Solution

The Answer

I use list comprehension a lot. One of them is this oneliner as below:

<?php
require_once(__DIR__.'/MyClassSongs.php');

$tags =
  array_values(
    array_unique(
      array_merge(
        ... array_map(
          function ($song) {
            return $song->tags; },
          $songs)
  )));

printf("%s\n", json_encode($tags));

Enough with introduction, at this point we should go straight to coding.

Environment

No need any special setup. Just run and voila..!


1: Data Structure Using Associative array

We are going to use associative array, throught out this article.

Simple Array

We can dump array with these option:

  • var_export()
  • print_r()
  • var_dump()
  • echo json_encode()

We are going to compare this. The choice is yours to choose.

Consider begin with simple array.

<?php
$tags = ["rock", "jazz", "rock", "pop", "pop"];

Then compare one by one:

var_export($tags);
print_r($tags);
var_dump($tags);
echo json_encode($tags);var_export($tags);

The last one, would result output similar to oneliner below:

❯ php 01-tags-d.php
["rock","jazz","rock","pop","pop"]

PHP: Output array using echo json_encode()

Associative Array

You can read about associative array in official documentation.

Consider this form.

<?php
$song = [ 'title' => 'Cantaloupe Island',
          'tags'  => ['60s', 'jazz'] ];

We can output with printf for prettier output

printf("%s: [%s]\n",
  $song['title'],
  join(', ', $song['tags']));

With the result as below:

❯ php 02-record-a.php
Cantaloupe Island: [60s, jazz]

Or we can use previous form using json_encode.

echo json_encode($song)."\n";

With the result as below:

❯ php 02-record-b.php
{"title":"Cantaloupe Island","tags":["60s","jazz"]}

PHP: Output associative array using json_encode

The Songs Structure

We can continue our journey to records just using dictionary. No need any complex structure.

<?php
$songs = [
  [ 'title' => 'Cantaloupe Island',
    'tags'  => ['60s', 'jazz'] ],
  [ 'title' => 'Let It Be',
    'tags'  => ['60s', 'rock'] ],
  [ 'title' => 'Knockin\' on Heaven\'s Door',
    'tags'  => ['70s', 'rock'] ],
  [ 'title' => 'Emotion',
    'tags'  => ['70s', 'pop'] ],
  [ 'title' => 'The River'],
];

foreach($songs as $song) {
  printf("%s\n", json_encode($song));
}

With the result similar as below record:

❯ php 03-songs.php
{"title":"Cantaloupe Island","tags":["60s","jazz"]}
{"title":"Let It Be","tags":["60s","rock"]}
{"title":"Knockin' on Heaven's Door","tags":["70s","rock"]}
{"title":"Emotion","tags":["70s","pop"]}
{"title":"The River"}

2: Separating Module

Since we need to reuse the songs record multiple times, it is a good idea to separate the record structure from logic.

Songs Module

The code can be shown as below:

<?php
$songs = [
  [ 'title' => 'Cantaloupe Island',
    'tags'  => ['60s', 'jazz'] ],
  [ 'title' => 'Let It Be',
    'tags'  => ['60s', 'rock'] ],
  [ 'title' => 'Knockin\' on Heaven\'s Door',
    'tags'  => ['70s', 'rock'] ],
  [ 'title' => 'Emotion',
    'tags'  => ['70s', 'pop'] ],
  [ 'title' => 'The River'],
];

PHP: The Songs Module Containing List of Record

Using Songs Module

Now we can have a very short code.

<?php
require_once(__DIR__.'/MySongs.php');

foreach($songs as $song) {
  printf("%s\n", json_encode($song));
}

Or alternatively:

<?php
require_once(__DIR__.'/MySongs.php');

array_walk($songs, function($song) {
  printf("%s\n", json_encode($song));
});

With the result as below associative array.

PHP: Using Songs Module

❯ php 04-module-b.php
{"title":"Cantaloupe Island","tags":["60s","jazz"]}
{"title":"Let It Be","tags":["60s","rock"]}
{"title":"Knockin' on Heaven's Door","tags":["70s","rock"]}
{"title":"Emotion","tags":["70s","pop"]}
{"title":"The River"}

3: Finishing The Task

Extract, Flatten, Unique

Extracting Data Structure

There are choices.

Consider this four different approaches:

<?php
require_once(__DIR__.'/MySongs.php');

$tagss = [];
foreach($songs as $song) {
  if(array_key_exists('tags', $song))
    $tagss[] = $song['tags'];
}

printf("%s\n", json_encode($tagss));

Alternatively we can obtain $tagss using this code below

<?php
function walk($song) {
  return isset($song['tags']) ?
    $song['tags'] : null;
}

$tagss = array_map('walk', $songs);

Or maybe this one is your preferred choice.

<?php
$songs = array_filter($songs, function ($song) {
  return isset($song['tags']);
});

$tagss = array_map(function ($song) {
  return $song['tags'];
}, $songs);

We can even write the last one as oneliner below:

<?php
$tagss = array_map(
  function ($song) { return $song['tags']; },
  array_filter($songs, function ($song) {
    return array_key_exists('tags', $song);
  })
);

With the result of array of array, as shown below.

❯ php 05-extract-ca.php
[["60s","jazz"],["60s","rock"],["70s","rock"],["70s","pop"]]

PHP: Extracting Dictionary

The choice is yours.

Flatten

There are also choices.

Consider this three different approaches:

The first is utilizing ... feature.

<?php
require_once(__DIR__.'/MySongs.php');

$tagss = [];
foreach($songs as $song) {
  if(array_key_exists('tags', $song))
    $tagss[] = $song['tags'];
}

$tags = array_merge(...$tagss);

printf("%s\n", json_encode($tags));

With the result of a flattened array shown below.

❯ php 06-flatten-a.php
["60s","jazz","60s","rock","70s","rock","70s","pop"]

We can rewrite above statement, using array_walk_recursive.

<?php
require_once(__DIR__.'/MySongs.php');

$tagss = [];
foreach($songs as $song) {
  if(array_key_exists('tags', $song))
    $tagss[] = $song['tags'];
}

function flatten(array $tagss) {
  $tags = array();
  array_walk_recursive(
    $tagss,
    function($element) use (&$tags) {
      $tags[] = $element;
    }
  );
  return $tags;
}

printf("%s\n", json_encode(flatten($tagss)));

Or maybe just flattening form the very start. This way should be more efficent.

<?php
require_once(__DIR__.'/MySongs.php');

$tags = [];
foreach($songs as $song) {
  if(array_key_exists('tags', $song))
    foreach($song['tags'] as $tag) {
      $tags[] = $tag;
    }
}

printf("%s\n", json_encode($tags));

With the result exactly the same as previous flattened array.

PHP: Flattening Array

Unique

To solve unique array, we can just use the array_unique function.

<?php
require_once(__DIR__.'/MySongs.php');

$tagss = [];
foreach($songs as $song) {
  if(array_key_exists('tags', $song))
    $tagss[] = $song['tags'];
}

$tags =
  array_values(
    array_unique(
      array_merge(
        ...$tagss
  )));

printf("%s\n", json_encode($tags));

With the result similar as below array:

❯ php 07-unique.php
["60s","jazz","rock","70s","pop"]

PHP: Solving Unique Song

I can live with that.


What is Next 🤔?

We have alternative way to build the record structure.

Consider continue reading [ PHP - Playing with Records - Part Two ].