Sunday 28 August 2016

Blimey, a CFML-centric article

G'day:
But it's nothing that interesting. I submitted my answer ("Friday puzzle: my PHP answer") for the Friday Code Puzzle yesterday, deciding to use PHP, given it's what I do for a crust these days, and I could best visualise the solution in PHP.

But the puzzle was from a CFML chat group, so I figured I might blow the dust of my CFML skills and see how closely a CFML answer would approximate my PHP one. The answer is "quite closely!". Here's a CFML transliteration of the PHP answer:

component {

    parents = {};

    function init() {
        parents[0]["children"] = [];

        return this;
    }

    static function loadFromCsv(filePath) {
        var dataFile = fileOpen(filePath, "read");

        var tree = new Tree();
        while(!fileIsEof(dataFile)){
            var line = fileReadLine(dataFile);
            tree.addNode(
                nodeText = line.listLast(),
                id = line.listFirst(),
                parent = line.listGetAt(2, ",", true)
            );
        }

        return tree;
    }

    private function addNode(nodeText, id, parent) {
        parent = parent == "" ? 0 : parent;

        parents[id].nodeText = nodeText;
        parents[parent].children = parents[parent].children ?: [];
        parents[parent].children.append(parents[id]);
    }

    function serializeJson() {
        return serializeJson(parents[0]["children"]);
    }
}

Note that this is using Lucee 5's CFML vernacular, and this will not run on ColdFusion for a coupla reasons:

  • ColdFusion still doesn't support static methods;
  • It has a new bug with the ?: operator which breaks some of my shortcuts there.

I did not write the full test suite, but I wrote a small test rig and ran all the test variations manually. Here's the trickiest one:

dataFile = expandPath("../../test/testfiles/notOrdered/data.csv");

tree = Tree::loadFromCsv(dataFile);
treeAsJson = tree.serializeJson();

jsonAsArray = deserializeJson(treeAsJson);
writeDump(jsonAsArray);

And it yields the goods:




For completeness, here's the variation I had to do to make this work on ColdFusion:

static function loadFromCsv(filePath) {
    var dataFile = fileOpen(filePath, "read");

    var tree = new Tree();
    while(!fileIsEof(dataFile)){
        var line = fileReadLine(dataFile);
        tree.addNode(
            nodeText = line.listLast(),
            id = line.listFirst(),
            parent = line.listGetAt(2, ",", true)
        );
    }
    return this;
}

private function addNode(nodeText, id, parent) {
    parent = parent == "" ? 0 : parent;

    parents[id] = parents.keyExists(id) ? parents[id] : {};
    parents[id].nodeText = nodeText;
    
    parents[parent] = parents.keyExists(parent) ? parents[parent] : {};
    parents[parent].children = parents[parent].keyExists("children") ? parents[parent].children : [];
    parents[parent].children.append(parents[id]);
}

  • ColdFusion does not yet support static methods, so I cannot use a static factory method to create the tree, I need to instantiate an empty tree and then get it to load itself. Not ideal, but not the end of the world either.
  • Because of various bugs in ColdFusion's implementation of implicitly creating "deep" data structures, and also with the ?: operator, I had to be more explicit with stepping through creation of intermediary structs, and also use keyExists instead of ?:

This isn't bad, and I don't fault ColdFusion for its lack of static yet, but I'm annoyed by finding yet more bugs in ColdFusion when all I want is to run some code.

Back to the Lucee version: I think it demonstrates CFML syntax is a bit tidier than PHP's, as it's not quite so in love with the punctuation, and punctuation is just noise to achieving good Clean Code. But in this example it's much of a muchness, innit?

Anyway, I have little else to add other than that. The approach is identical to the PHP one I did: I just wanted to compare the two languages.

Job done.

Righto.

--
Adam

Saturday 27 August 2016

Friday puzzle: my PHP answer

G'day:
So as per my previous article - "Friday code puzzle" - here's my answer to the current puzzle.

I knocked the first version of this out over a beer whilst waiting for my flight to Galway, last night. Then scrapped that as being crap, and rewrote whilst in transit. And then... erm... back-filled my unit tests y/day evening. I feel guilty about not doing proper TDD,  but... so be it. It's got test coverage now.

Just on TDD for a second. It was wrong of me not to do the testing first, and whilst I was just about to manufacture an excuse as to why not ("I didn't have PHPUnit installed on this machine", "I couldn't be arsed making a whole project out of it", "um [something about being offline on the flight]"), it was all bullshit laziness. I had to write tests to prove that my function would work outside of the immediate quiz requirements: the easiest way to do this is with unit tests and clear expectations etc. These tests were of limited benefit to me, given I already had the code written, and had already been through the trial-and-error process of getting the thing working correctly.  The thing is, in a professional environment the tests aren't for you necessarily. They're for the next person who comes along to maintain your code. Or to troubleshoot your code when an edge-case crops up. It should not be down to the next person to write the tests for your code. That's lazy-arse, ego-centric shit. Do your job properly. I have encountered a coupla devs recently who think they're too good for tests, and refuse to do them properly. I don't want to work with people like that as they're all "me me me me".

But anyway... some code.

Well first a recap. Here's the "official" description from Jesse. But in summary:

We have this data set:

id,parentId,nodeText
1,,File
2,1,New
3,2,File
4,2,Folder
5,,Edit
6,5,Copy
7,5,Cut
8,5,Paste
9,,Help
10,9,About

This reflects hierarchical data (I would not represent a hierarchy that way, but that's a different story for another time), and we want to convert it to a hierarchical representation, eg:

[
  {
     nodeText : "File"
    ,children : [
      {
         nodeText : "New"
        ,children : [
           { nodeText: "File" }
          ,{ nodeText: "Folder" }
        ]
      }
    ]
  }
  ,{
     nodeText : "Edit"
    ,children : [
       {nodeText: "Copy"}
      ,{nodeText: "Cut"}
      ,{nodeText: "Paste"}
    ]
  }
  ,{
     nodeText : "Help"
    ,children : [
      { nodeText: "About" }
    ]
  }
]

There are a few "rules of engagement" at that link I mentioned, but that's the gist of it.

I decided I had better write some PHP code for a change, so knocked something together hastily. Initially I thought I might need to write some recursive monster to generate the hierarchy, but instead realised I did not need to do that, I just needed to track each "parent" node as I encountered it, and then append children to it as appropriate. Note that the data is sorted, so each record could be a parent of a subsequent node, and it will never be the case one will encounter a node before already having processed its parent. So all I needed to do is iterate over the data from top to bottom. Once. Nothing more tricky than that. Then by the time I had finished, the first parent would represent the entire tree. That was easy enough but then I had to convert it to JSON. Note the above representation is not JSON, but it's close enough, so that's what I decided to run with. As it turns out this was pretty easy to achieve in PHP as it has the ability to provide a custom serialiser for a class, and given I'd used a mix of associative and indexed arrays to build the data structure, it was simply a matter of returning the first parent node's children. Done.

Enough chatter, here's the code...

Update:

I have updated this from the first version I posted. This is slightly simpler, and also works even if the data is not pre-sorted. Thanks to Mingo for encouraging me to look into this.


<?php

namespace puzzle;

class Tree implements \JsonSerializable {

    private $parents = [];

    function __construct() {
        $tree = [
            "children" => []
        ];
        $this->parents[0] = $tree;
    }

    static function loadFromCsv($filePath) {
        $dataFile = fopen($filePath, "r");

        $tree = new Tree();
        while(list($id, $parent, $nodeText) = fgetcsv($dataFile)) {
            $tree->addNode($nodeText, $id, $parent);
        }

        return $tree;
    }

    private function addNode($nodeText, $id, $parent) {
        $parent = $parent === "" ? 0 : $parent;

        $this->parents[$id]["nodeText"] = $nodeText;
        $this->parents[$parent]["children"][] = &$this->parents[$id];
    }

    function jsonSerialize() {
        return $this->parents[0]["children"];
    }
}

The only other thing to note here is that the requirements indicated the data "should be in the form of an RDBMS resultset", but I could not be arsed horsing around with DB data for this, so I'm just reading a CSV file instead.

I also wrote the tests, as I said:

<?php

namespace test\puzzle;

use puzzle\Tree;

/** @coversDefaultClass \puzzle\Tree */
class TreeTest extends \PHPUnit_Framework_TestCase {

    private $testDir;

    function setup() {
        $this->testDir = realpath(__DIR__ . "/testfiles");
    }

    /**
     * @covers ::loadFromCsv
     * @covers ::__construct
     * @covers ::addNode
     * @covers ::jsonSerialize
     * @dataProvider provideCasesForLoadFromCsvTests
     */
    function testLoadFromCsv($baseFile){
        $files = $this->getFilesFromBase($baseFile);

        $result = Tree::loadFromCsv($files["src"]);
        $resultAsJson = json_encode($result);
        $resultAsArray = json_decode($resultAsJson);

        $expectedJson = file_get_contents($files["expectation"]);
        $expectedAsArray = json_decode($expectedJson);

        $this->assertEquals($expectedAsArray, $resultAsArray);
    }

    private function getFilesFromBase($base){
        return [
            "src" => sprintf("%s/%s/data.csv", $this->testDir, $base),
            "expectation" => sprintf("%s/%s/expected.json", $this->testDir, $base)
        ];
    }

    function provideCasesForLoadFromCsvTests(){
        return [
            "puzzle requirements" => ["testSet" => "puzzle"],
            "one element" => ["testSet" => "one"],
            "deep" => ["testSet" => "deep"],
            "flat" => ["testSet" => "flat"],
            "not ordered" =&gt ["testSet" =&gt "notOrdered"]
        ];
    }
}

There's no real surprise of discussion point there, beside highlighting the test cases I decided upon:

  • the puzzle requirements;
  • a single element;
  • a deep structure;
  • a flat structure.

I think those covered all the bases for a Friday Puzzle. An example of the data and expectation are thus (this is the "deep" example):

1,,Tahi
2,1,Rua
3,2,Toru
4,3,Wha
5,4,Rima
6,5,Ono
7,6,Whitu
8,7,Waru
9,8,Iwa
10,9,Tekau


[
  {
    "nodeText": "Tahi",
    "children": [
      {
        "nodeText": "Rua",
        "children": [
          {
            "nodeText": "Toru",
            "children": [
              {
                "nodeText": "Wha",
                "children": [
                  {
                    "nodeText": "Rima",
                    "children": [
                      {
                        "nodeText": "Ono",
                        "children": [
                          {
                            "nodeText": "Whitu",
                            "children": [
                              {
                                "nodeText": "Waru",
                                "children": [
                                  {
                                    "nodeText": "Iwa",
                                    "children": [
                                      {
                                        "nodeText": "Tekau"
                                      }
                                    ]
                                  }
                                ]
                              }
                            ]
                          }
                        ]
                      }
                    ]
                  }
                ]
              }
            ]
          }
        ]
      }
    ]
  }
]

All the rest are on GitHub.

I ran the code coverage report on the tests, and they're all green:


So that's that: surprisingly simple once I got into my head how to approach it.

Give it a blast. Reminder: the puzzle details are in this gist.

Righto.

--
Adam

Friday 26 August 2016

Friday code puzzle

G'day:

I'm waiting for a blood test and I'm "customer number 649", and they are currently processing "customer number 541". I have dubbed this place Franz Kafka Memorial Hospital.

Anyway, I have an hour or two to kill, so writing this quicky on my phone.

Last week on the CFML Slack Channel we piloted the notion of a Friday Puzzle. It went quite well, so we're continuing it. Don't be put off by the fact it's the CFML Slack Channel: we actively encourage our community to use any language they like, or different languages from usual, as it's a good way to improve one's skills as a programmer. I'll be doing this week's one in PHP. And maybe diversifying from there into something else.

This week's puzzle is as follows (in summary):

Challenge:

Convert a flat representation of a hierarchical data into a nested tree structure.

[...]

dataset:

idparentIdnodeText
1nullFile
2 1New
32File
42Folder
5 nullEdit
65Copy
75Cut
85Paste
9nullHelp
109About


Expected result:


[ 
    { 
        nodeText : "File",
        children : [ 
            { 
                nodeText : "New",
                children : [ 
                    {nodeText: "File"},
                    {nodeText: "Folder"}
                ] 
            }
        ] 
    },{ 
        nodeText : "Edit",
            children : [ 
                {nodeText: "Copy"},
                {nodeText: "Cut"},
                {nodeText: "Paste"}
            ]
    },{ 
        nodeText: "Help",
            children : [
                { nodeText: "About"}
            ]
    } 
]


I've omitted the rules and expectations and stuff. Full details are on the Slack Channel. You can sign-up at: http://cfml-slack.herokuapp.com/;

Give it a go!

Oh... And they're now processing customer 585.

Righto.

--
Adam

Thursday 18 August 2016

Breaking: Groovy and Clojure answers for that array-look-up code puzzle

G'day:
Well it's not really that breaking really... what I mean is a coupla other people posted some answers to last week's code puzzle after I wrote up the results ("Looking at the code from that code puzzle last week"). I was gonna just append them to the bottom of that earlier article, but hen no-one would see them, and that seemed a bit disrespectful. Also for this blog which is still mostly read by CFML devs, it's some exposure to other languages you might want to take the time to look at, and these are good examples why.

Sean

I was wondering what happened to Sean's Clojure example, but he was off leading his life instead of clearing away the cobwebs from my blog, so didn't notice the code puzzle initially. But here's his Clojure code (fully annotated for us non-Clojurians):

;; simplest solution to find first match -- note that `filter` returns
;; a lazy chunked sequence so it will not search the entire vector
;; however, the chunks are 32 elements in size so it will search up
;; to 31 elements beyond the first match
(first (filter #(re-find #".+at" %) ["at" "cat" "scat" "scratch"]))

;; Clojure has `some` but it returns the result of applying the predicate
;; not the original element so we need to write a "smarter" predicate:

;; will not work in all cases:
(some #(re-find #".+at" %) ["at" "cat" "scat" "scratch"])
;; this: (some #(re-find #".+at" %) ["scratch" "at" "cat" "scat"])
;; produces this: "scrat" -- oops!

;; will work with extended predicate:
(some #(when (re-find #".+at" %) %) ["at" "cat" "scat" "scratch"])

;; or we can use reduce with an early return -- the `reduced` value:
(reduce (fn [_ s] (when (re-find #".+at" s) (reduced s))) nil ["at" "cat" "scat" "scratch"])

;; a note about notation: #(.. % ..) is shorthand for (fn [x] (.. x ..))
;; i.e., an anonymous function with one argument

That looks like a bunch of code, but it's also four examples:

(first (filter #(re-find #".+at" %) ["at" "cat" "scat" "scratch"]))

(some #(re-find #".+at" %) ["at" "cat" "scat" "scratch"])

(some #(when (re-find #".+at" %) %) ["at" "cat" "scat" "scratch"])

(reduce (fn [_ s] (when (re-find #".+at" s) (reduced s))) nil ["at" "cat" "scat" "scratch"])


Now I've only had the most superficial look at Clojure, but even I can just read what's going on in that code. So that's cool. I've been off my game recently with my out-of-hours tech stuff - in case you hadn't noticed - and I really want to finish finding my motivation to get back to it, and look at more Clojure. I think it's a good thing to look at for a perennial CFMLer or PHPer as its quite the paradigm shift, but still seems pretty easy to get at least a superficial handle on, and then work from there.

Tony

Tony's done a Groovy example. Every time I see Groovy, it just seems cool. Check this out:

print( ['a', 'at', 'cat', 'scat', 'catch'].find { it ==~ '.+at' } )

That's it. Done. 67 characters, most of it data. 25 characters of actually "doing stuff", including more whitespace than I'd usually use for this sort of thing. Doesn't that make you want to use Groovy?

Anyway, that's that. I just wanted to share that code with y'all.

Righto.

--
Adam

Tuesday 16 August 2016

Looking at the code from that puzzle last week

G'day:
So last Fri I asked this:

[...] here's a code puzzle.

Rules:
  • You have an array of strings, eg: ['a', 'at', 'cat', 'scat', 'catch'].
  • Return the first value that matches a regex pattern, eg: '.+at' would match cat, scat, catch; but we want cat returned.
  • Do not use any looping statements (eg: do/for/while etc).
  • Bear in mind functions are not statements ;-)
  • The array could be very long, the strings could be very long, and the pattern could be complex. But the desired value could be early in the array.
  • Use any language.

I didn't have much of a reason for asking. I had to do something similar at work, except the match criteria were more complicated, and I tried a few ways and didn't like any of them. When processing a "plural" data structure - and array or a struct or some other sort of collection - I like to avoid generic looping statements if I can, as I don't think they're terribly declarative. If one needs to somehow transform a collection into something else, I prefer to use collection-iteration methods or functions like map, reduce, filter etc. I initially did this sort of thing (JavaScript):

var words = ["a", "at", "cat", "scat", "catch"];

var match = words.reduce(function(match, word){
    if (match !== undefined) return match;
    if (word.match(/.+at/)){
        return word;
    }
}, undefined);

console.log(match);

That's all good in theory. If one is taking a collection and the data processing of it returns a single (different) value, then reduce makes sense. The problem is that reduce iterates over the entire collection whether one needs to or not, so after I've found "cat" I'm just wasting effort looking at "scat" and "catch". Now I'm not one to worry about wasting cycles and that sort of micro-optimisation, but it still didn't sit well with me.

So next I considered using the "early exit" iteration method, some. I can stop that when I've finished. The problem is that some returns a boolean. And I needed a cat. But I could solve that with some closure:

var match;
words.some(function(word){
    if (word.match(/.+at/)){
        return match = word;
    }
});
console.log(match);

Note that return statement contains an assignment, and that evaluates to true, thus exiting the collection iteration (some ends as soon as the callback returns true).

That's all well and good, except I actually needed this done in PHP and it doesn't have a some function.

In the end I just used a foreach loop and was done with it:

$words = ["a", "at", "cat", "scat", "catch"];

$match = null;
foreach($words as $word) {
    if (preg_match("/.+at/", $word) === 1) {
        $match = $word;
        break;
    }
}

echo $match;

Still: all this got me thinking it was an interesting exercise. Well: kinda. So posted the puzzle to see what people came up with.

I added the constraint of no looping statements to push people towards using something more interesting. It was an artificial constraint.

Anyway, enough about me. Let's have a look at other people's code.

Isaiah

First up was Isaiah with a Ruby answer. This is short and sweet:

puts %w(a at cat scat catch).detect{|w| w =~ /.+at/ }

(I added the puts so I could check it output something)

detect is exactly what I needed for this. As per the detect docs:

Passes each entry in enum to block. Returns the first for which block is not false.
So like a combination of some and reduce, really. Cool. This answer is gonna be hard to beat (NB: I am only looking at the answers for the first time now!).

Brad

Next was Brad with a CFML answer:

echo( ['a', 'at', 'cat', 'scat', 'catch'].filter( function( i ) { return reFind( '.+at', i ) } ).first() )

This one only runs on Lucee. This slight revision works on CF2016 as well:

words = ['a', 'at', 'cat', 'scat', 'catch'];
writeOutput( words.filter( function( i ) { return reFind( '.+at', i ); } )[1] );

Brad's answer falls into the same trap my reduce version did: it keeps iterating after it could stop.

I do like how terse Brad's answer is here. Although it's borderline (borderline) unreadable. It demonstrates CFML can do some good stuff though.

Jesse

Jesse's JavaScript solution is next, once again going for the terseness prize:

console.log(['a', 'at', 'cat', 'scat', 'catch'].find((e)=>/.+at/.test(e)));

(again... the console.log is my addition).

I didn't know about the find method I have to admit, so that's cool. This is the equivalent of Isaiah's Ruby answer.

Bonus points for using an arrow function there!

Tyler

Tyler has done another JavaScript solution, this time using filter:

function getFirstArrayRegexMatch(array, regex){
  return (
    array.filter(function(element){
      if(element.match(regex)){ return element; }
    })[0]
  );
}

var a = ["a","at","cat","scat","catch"]
,   r = /.+at/;

document.write(getFirstArrayRegexMatch(a, r));

Bonus point for putting it in a function.

Ryan

Ryan's given me two JavaScript answers: one short-hand, one long-hand with a bunch of testing.

var firstMatch = (input, pattern) => input.find(item => item.match(pattern));

console.log(firstMatch(['a', 'at', 'cat', 'scat', 'catch'], '.+at'));

Similar to Jesse's answer.

function f (input, pattern) {
    var reverse = str => str.split('').reverse().join('');
    var reversedIndex = (l, i) => l - (i + 1);
    
    var j = '\n' + input.join('\n') + '\n',
        i = j.search(pattern);

    if (i == -1) return undefined;

    var end = j.indexOf('\n', i);
    var start = reversedIndex(j.length, reverse(j).indexOf('\n', reversedIndex(j.length, end) + 1));
    return j.substr(start + 1, end - start - 1);
}

console.log(f(['a', 'at', 'cat', 'scat', 'catch'], '.+at'));
console.log(f(['a', 'at', 'cat', 'scat', 'catch'], '.+cat'));
console.log(f(['a', 'at', 'cat', 'scat', 'catch'], '.t'));
console.log(f(['a', 'at', 'cat', 'scat', 'catch'], 'a'));
console.log(f(['a', 'at', 'cat', 'scat', 'catch'], '.+xat'));

This shows that Ryan paid attention to the requirements. I specifically said the answer might be late in the array, and he's catering for that here.

Plus tests! Including unhappy path tests!

Ryan knows how to get bonus points from me, eh?

Choop

Choop's used CFML. It's interesting how most of my readership is still made up of CFMLers, but most of them here have chosen not to use it. Well: a bit interesting.

/* lucee or adobe CF 11+  */
mystrings = ['a', 'at', 'cat', 'scat', 'catch'];
mytest = '.+at';
function firstMatch( strings, regex ) {
    var test = arguments.regex;
    var passing = arguments.strings.filter( function ( check ) {
        var matches = REMatch( test, check );
        return matches.Len() > 0;
    });
    return passing[ 1 ];
}
WriteOutput( firstMatch( mystrings, mytest ) );

Choop's eschewed terseness in favour of writing good clear code.

Mingo

Next up is Mingo's answer. I clearly spoke too soon before as he's stuck with CFML too. He's taken the reduce route, similar to my approach:

  arrayOfStrings = [ 'a', 'at', 'cat', 'scat', 'catch' ];
  regex = '.+at';
  writeOutput( arrayOfStrings.reduce( function( result='', item ){ return len( result ) ? result : item.reFind( regex ) ? item : ''; } ) );

Adam Tuttle made a knowing comment against this one:

I thought about nested ternaries too but figured Adam would chastise me for it. ;)
And yer bloody right! ;-)

I'm all good for a single ternary expression, but as soon as there's more than one... I think everyone's getting confused pretty quickly?

Adam

And indeed Adam's own JavaScript entry is up next. Props for using ES2015 constructs. I especially like how it can now do interpolated strings. Like CFML has been doing for about 20yrs.

"use strict"

function firstMatch ( pattern, data ) {
  return data.reduce( ( prev, curr ) => {
      if ( prev.length > 0 ) { return prev }
      if ( pattern.test( curr ) ) { return curr }
      return prev
  }, '')
}

const pattern = /.+at/
let data = ['a', 'at', 'cat', 'scat', 'catch']

console.log( `the answer is: ${firstMatch( pattern, data )}` )

His logic is very similar to my own reduce example too. (Oh, I needed to add "use strict" to that code to get it to run on my node install. Not sure why... it might be old).

As I am writing this up, I have noticed two entries stuck in my moderation queue. Sorry about that.

Eric

Here's Eric's CFML answer:

    function first( arr, predicate, defaultValue ) {
        if ( arrayIsEmpty( arr ) ) {
            if ( ! isNull( defaultValue ) ) {
                return defaultValue;
            }

            throw( "Cannot return the result because the array is either empty or no value matched the predicate with no default value provided." );
        }

        if ( isNull( predicate ) ) {
            return arr[ 1 ];
        } else {
            arguments.arr = arr.filter( predicate );
            structDelete( arguments, "predicate" );
            return first( argumentCollection = arguments );
        }
    }
    
    answer = first( ['a', 'at', 'cat', 'scat', 'catch'], function( str ) {
        return reFind( '.+at', str );
    } );
    
    writeOutput( answer );

Good to see some validation going on in there. I also really like how his own function takes a callback to do the check for what constitutes a match. Bloody nice that.

Quan Tran

Coincidentally the most interesting answer is the last one (it would not have been last had it not got stuck in the moderation queue, that said). Here's Quan Tran's recursive CFML answer:

// http://blog.adamcameron.me/2016/08/code-quiz.html
function regexSearchArray(regexString,arrStrings){
    var localArrStrings = arrStrings;
    
    if (not arrayLen(localArrStrings))
        return;
    else if (refind(regexString, localArrStrings[1]))
        return localArrStrings[1];
    else{
        ArrayDeleteAt(localArrStrings,1);
        return regexSearchArray(regexString,localArrStrings);
    }
}
arrStrings = ['a', 'at', 'cat', 'scat', 'catch'];
writeoutput(regexSearchArray('.+at',arrStrings));

That is a very inventive way of getting around my "no loop statements" rule. Very impressed.



That's quite a few entries for a wee code puzzle on this blog (the readership of which has pretty much died since I moved from CFML, even allowing for the distinct lack of content recently). So thanks for that.

I think all these answers had merit and discussion points and had something interesting about them. I like how terse Jesse and others managed to get their answers. I liked how Isaiah used Ruby instead of the usual suspects (for this blog, anyhow... not much Ruby going on around here). I especially like how Ryan provided more tests.

But the winner is Quan Tran with his recursive solution. It might not be the most performant, but it's def the most interesting.

Cheers all. I have a few other dumb-arse quiz questions I might continue to ask. We'll see.

Righto.

--
Adam

Friday 12 August 2016

Code puzzle

G'day:
Apropos of nothing, here's a code puzzle.

Rules:
  • You have an array of strings, eg: ['a', 'at', 'cat', 'scat', 'catch'].
  • Return the first value that matches a regex pattern, eg: '.+at' would match cat, scat, catch; but we want cat returned.
  • Do not use any looping statements (eg: do/for/while etc).
  • Bear in mind functions are not statements ;-)
  • The array could be very long, the strings could be very long, and the pattern could be complex. But the desired value could be early in the array.
  • Use any language.

The prize:
Nothing really. Well: nothing at all.

Put yer answer in a Gist (or similar) and the URL to the Gist in a comment. IE: do not post code in a comment.

Righto.

--
Adam