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: A practical case to collect unique record fields using Lua.

Lua capability is somehow limited and less attractive. This is a good challenge. And I finally find out that Lua can do more than I thought.

Since Lua does not come natively with map, filter, and reduce, we should write code with imperative approach.

Lua has a free functional libary, called fun. This library is good, but implement the code comes at price, when it comes to convert <generator> back to array. But don’t worry, this case example is enough for you, to code in functional fashioned.

Reference Reading

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

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" }
}

Lua Solution

The Answer

There might be many ways to do things in Lua. One of them is this oneliner as below:

import mysongs._

…

print(inspect(unique(flatten(songs))))

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

The array data type is common in lua.

Simple Array

Before building a records, I begin with simple array.

local inspect = require('inspect')
-- https://github.com/kikito/inspect.lua

tags = {"rock", "jazz", "rock", "pop", "pop"}
print(tags)

With the result similar as below:

$ lua 01-tags.lua
table: 0x5574a7a78650

Or better, using luajit:

$ luajit 01-tags.lua
{ "rock", "jazz", "rock", "pop", "pop" }

Inspect Library

To dump variable in lua, we can utilize inspect library that is available at:

First we have to include the module

local inspect = require('inspect')

Then use the inspect function in the code.

local inspect = require('inspect')

tags = {"rock", "jazz", "rock", "pop", "pop"}
print(inspect(tags))

With the result similar as below array:

$ lua 01-tags.lua
{ "rock", "jazz", "rock", "pop", "pop" }

Lua: Simple List of Tags

The Song Record

We can continue our journey to records. Lua has associative array called table, as shown below:

local inspect = require('inspect')

song = { title = "Cantaloupe Island",
         tags  = {"60s", "jazz"} }

print(inspect(song))

With the result similar as below record:

$ lua 02-record.lua
{
  tags = { "60s", "jazz" },
  title = "Cantaloupe Island"
}

List of Song Record

Meet The Songs List

From just karaoke, we can go pro with recording studio. From simple data, we can build a structure to solve our task.

Consider make this data stucture more complex, as shown in below code:

local inspect = require('inspect')

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" }
}

for _, song in pairs(songs) do
  print(inspect(song))
end

_.

With the result similar as below array:

$ lua 03-songs.lua
{
  tags = { "60s", "jazz" },
  title = "Cantaloupe Island"
}
{
  tags = { "60s", "rock" },
  title = "Let It Be"
}
{
  tags = { "70s", "rock" },
  title = "Knockin' on Heaven's Door"
}
{
  tags = { "70s", "pop" },
  title = "Emotion"
}
{
  title = "The River"
}

The Fun Library

There is a functional library available at:

First we have to include the module

local fun     = require('fun')

Now we can write the code in functional fashioned, as shown below:

local inspect = require('inspect')
local fun     = require('fun')

…

-- compose
function print_inspect(x)
  print(inspect(x))
end

fun.each(print_inspect, songs)

With exactly the same result as above code.


2: Separating Module

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

Songs Module

The code can be shown as below:

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" }
}

Lua: The Songs Module Containing List of Record

Using Songs Module

Now we can have a very short code.

ilocal inspect = require('inspect')
local fun     = require('fun')

require "my-songs"

-- compose
function print_inspect(x)
  print(inspect(x))
end

fun.each(print_inspect, songs)

With the result exactly the same as previous code.


3: Using Fun Library

Towards Functional

Imperative/Procedural

Consider this loop:

local inspect = require('inspect')
require "my-songs"

for _, song in ipairs(songs)
do
   print(inspect(song.tags))
end

_.

With the result similar as below array:

$ lua 05-map.lua
{ "60s", "jazz" }
{ "60s", "rock" }
{ "70s", "rock" }
{ "70s", "pop" }
nil

Importing Fun Library

There are other way to refer function using require.

local inspect = require('inspect')
require 'fun' ()
require 'my-songs'

This way, we can just call each, instead of fun.each.

Functional Approach

We can rewrite using fun library, so we have functional fashioned code.

local inspect = require('inspect')
require 'fun' ()
require 'my-songs'

function print_inspect(x)
  print(inspect(x))
end

local tags = map(
  function (song) return song.tags end,
  songs
)

each(print_inspect, tags)

With the result exactly the same as previous code.

Filter

We can go further stripping nil value.

local inspect = require('inspect')
require 'fun' ()
require 'my-songs'

function print_inspect(x)
  print(inspect(x))
end

function extract(songs)
  return map(
    function (song) return song.tags end, songs)
end

function clean(tags)
  return filter(
    function (tag) return tag end, tags)
end

each(print_inspect, clean(extract(songs)))

With the result similar as below array:

$ lua 06-filter.lua
{ "60s", "jazz" }
{ "60s", "rock" }
{ "70s", "rock" }
{ "70s", "pop" }

Lua: Using Functional Library

So far so good. We are going to face issue, sooner or later.


4: Imperative Approach

Flatten and Unique

We can actually, discard the whole functional library above, and making our own custom script.

Flatten using Loop

All at Once

Just like anty other functional programming, we can have a lot of fun with map. Then get the default option value with pattern matching.

local inspect = require('inspect')
require 'my-songs'

function flatten(songs)
  local tags = {}

  for _, song in pairs(songs)
  do
    local song_tags = song.tags

    if type(song_tags) == 'table' then
      for _, tag in pairs(song_tags)
      do
        tags[#tags + 1] = tag
      end
    end

  end
  
  return tags
end

print(inspect(flatten(songs)))

With the result similar as below array:

$ lua 07-flatten.lua
{ "60s", "jazz", "60s", "rock", "70s", "rock", "70s", "pop" }

Lua: Flatten Array Using Loop

Unique Elements

I found the right script from the internet:

Now I can apply to my code.

local inspect = require('inspect')
require 'my-songs'

function flatten(songs)
  …
end

function unique(tags)
  local hash, res = {}, {}

  for _,tag in pairs(tags) do
    if (not hash[tag]) then
      res[#res+1] = tag
      hash[tag] = true
    end
  end
  
  return res
end

print(inspect(unique(flatten(songs))))

_.

With the result similar as below array:

$ lua 08-unique.lua
{ "60s", "jazz", "rock", "70s", "pop" }

Succeed.

It is done. Task finished. Mission accomplished.


What is Next 🤔?

We should be ready for our next topic, concurrency case example.

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