Skip to content

Instantly share code, notes, and snippets.

@hakre
Created June 29, 2012 16:16
Show Gist options
  • Save hakre/3018902 to your computer and use it in GitHub Desktop.
Save hakre/3018902 to your computer and use it in GitHub Desktop.
convert tab/space delimited lines into nested array
<?php
/**
* convert tab/space delimited lines into nested array
* @link http://stackoverflow.com/q/9797261/367456
* @author hakre <http://hakre.wordpress.com>
*/
$input = <<<STR
cat, true cat
=> domestic cat, house cat, Felis domesticus, Felis catus
=> kitty, kitty-cat, puss
=> mouser
=> alley cat
=> tom, tomcat
=> gib
=> Angora, Angora cat
=> Siamese cat, Siamese
=> blue point Siamese
=> wildcat
=> sand cat
=> European wildcat, catamountain, Felis silvestris
=> cougar, puma, catamount, mountain lion, painter, panther, Felis concolor
=> ocelot, panther cat, Felis pardalis
=> manul, Pallas's cat, Felis manul
=> lynx, catamount
=> common lynx, Lynx lynx
=> Canada lynx, Lynx canadensis
STR;
// build tree
$tree = $levels = array();
$stack[1] = &$tree;
for ($line = strtok($input, $token = "\n"); $line; $line = strtok($token)) {
if (!preg_match('/^(?:(\s*)=> )?(.*)$/', $line, $self)) {
continue;
}
array_shift($self);
$indent = array_shift($self);
$level = @$levels[$indent] ? : $levels[$indent] = count($levels) + 1;
$stack[$level][] = &$self;
$stack[$level + 1] = &$self[];
unset($self);
}
unset($stack);
// output
tree_print($tree);
function tree_print(array $tree, $in = '') {
echo "$in<ul>\n";
$i = $in . ' ';
foreach ($tree as $n)
printf("</li>\n", printf("$i<li>$n[0]") && $n[1] && printf($i, printf("\n") & tree_print($n[1], "$i ")));
echo "$in</ul>\n";
}
<?php
/**
* convert tab/space delimited lines into nested array
* @link http://stackoverflow.com/q/9797261/367456
* @author hakre <http://hakre.wordpress.com>
*/
$input = <<<STR
cat, true cat
=> domestic cat, house cat, Felis domesticus, Felis catus
=> kitty, kitty-cat, puss
=> mouser
=> alley cat
=> tom, tomcat
=> gib
=> Angora, Angora cat
=> Siamese cat, Siamese
=> blue point Siamese
=> wildcat
=> sand cat
=> European wildcat, catamountain, Felis silvestris
=> cougar, puma, catamount, mountain lion, painter, panther, Felis concolor
=> ocelot, panther cat, Felis pardalis
=> manul, Pallas's cat, Felis manul
=> lynx, catamount
=> common lynx, Lynx lynx
=> Canada lynx, Lynx canadensis
STR;
echo_list($input);
function echo_list($string) {
foreach ($m = array_map(function($v) use (&$l) {
return array(@$l[$i = &$v[1]] ? : $l[$i] = count($l) + 1, $v[2]);
}, preg_match_all('/^(?:(\s*)=> )?(.*)$/m', $string, $m, PREG_SET_ORDER) ? $m : array()) as $i => $v) {
$pi = str_repeat(" ", $pl = @$m[$i - 1][0]); # prev
$ni = str_repeat(" ", $nl = @$m[$i + 1][0]); # next
(($v[0] - $pl) > 0) && printf("$pi<ul>\n"); # is child of prev
echo ' ' . str_repeat(" ", $v[0] - 1), "<li>$v[1]"; # output self
if (!$close = (($nl - $v[0]) * -1)) echo "</li>\n"; # has sibling
else if ($close < 0) echo "\n"; # has children
else for (printf("</li>\n$ni" . str_repeat(" ", $close - 1) . "</ul>\n"); --$close;) # is last child
echo $ni, $nn = str_repeat(" ", $close - 1), " </li>\n",
$ni, $nn, "</ul>\n";
}
}
<?php
/**
* convert tab/space delimited lines into nested array
* @link http://stackoverflow.com/q/9797261/367456
* @author hakre <http://hakre.wordpress.com>
*/
$input = <<<STR
cat, true cat
=> domestic cat, house cat, Felis domesticus, Felis catus
=> kitty, kitty-cat, puss
=> mouser
=> alley cat
=> tom, tomcat
=> gib
=> Angora, Angora cat
=> Siamese cat, Siamese
=> blue point Siamese
=> wildcat
=> sand cat
=> European wildcat, catamountain, Felis silvestris
=> cougar, puma, catamount, mountain lion, painter, panther, Felis concolor
=> ocelot, panther cat, Felis pardalis
=> manul, Pallas's cat, Felis manul
=> lynx, catamount
=> common lynx, Lynx lynx
=> Canada lynx, Lynx canadensis
STR;
/**
* Levels for indent strings
*/
class Levels
{
private $levels = array();
public function __invoke($str)
{
$number = strlen($str);
$key = array_search($number, $this->levels, true);
if ($key !== false) {
return $key;
}
$key = count($this->levels);
if ($key && $number < $this->levels[$key - 1]) {
throw new InvalidArgumentException(sprintf(
'The number given %d is not a level yet but below the range of levels which ends at %d.',
$number, $this->levels[$key - 1]
));
}
$this->levels[$key] = $number;
return $key;
}
}
/**
* preg match as array
*
* @param $string
* @param string $pattern pcre regular expression pattern.
* @return array
*/
$matches = function($string, $pattern)
{
return preg_match_all($pattern, $string, $matches, PREG_SET_ORDER)
? $matches : array();
};
$map = function(Levels $levels)
{
return function (array $match) use ($levels)
{
list(, $indent, $name) = $match;
$level = $levels($indent);
return array($level, $name);
};
};
$map = $map(new Levels());
$tree = array();
$stack = function(array &$tree)
{
$stack[] = &$tree;
return function($level, $element) use (&$stack)
{
$self = array($element, array());
$stack[$level][] = &$self;
$stack[$level + 1] = &$self[1];
};
};
$push = $stack($tree);
foreach ($matches($input, '/^(?:(\s*)=> )?(.*)$/m') as $match) {
list($level, $element) = $map($match);
$push($level, $element);
}
class TreeIterator extends ArrayIterator implements RecursiveIterator
{
private $current;
public function __construct($node)
{
parent::__construct($node);
}
public function current()
{
$this->current = parent::current();
return $this->current[0];
}
public function hasChildren()
{
return !empty($this->current[1]);
}
public function getChildren()
{
return new self($this->current[1]);
}
}
$treeIterator = new RecursiveTreeIterator(
new TreeIterator($tree));
foreach ($treeIterator as $val) echo $val, "\n";
array (
0 =>
array (
0 => 'cat, true cat',
1 =>
array (
0 =>
array (
0 => 'domestic cat, house cat, Felis domesticus, Felis catus',
1 =>
array (
0 =>
array (
0 => 'kitty, kitty-cat, puss',
1 => NULL,
),
1 =>
array (
0 => 'mouser',
1 => NULL,
),
2 =>
array (
0 => 'alley cat',
1 => NULL,
),
3 =>
array (
0 => 'tom, tomcat',
1 =>
array (
0 =>
array (
0 => 'gib',
1 => NULL,
),
),
),
4 =>
array (
0 => 'Angora, Angora cat',
1 => NULL,
),
5 =>
array (
0 => 'Siamese cat, Siamese',
1 =>
array (
0 =>
array (
0 => 'blue point Siamese',
1 => NULL,
),
),
),
),
),
1 =>
array (
0 => 'wildcat',
1 =>
array (
0 =>
array (
0 => 'sand cat',
1 => NULL,
),
1 =>
array (
0 => 'European wildcat, catamountain, Felis silvestris',
1 => NULL,
),
2 =>
array (
0 => 'cougar, puma, catamount, mountain lion, painter, panther, Felis concolor',
1 => NULL,
),
3 =>
array (
0 => 'ocelot, panther cat, Felis pardalis',
1 => NULL,
),
4 =>
array (
0 => 'manul, Pallas\'s cat, Felis manul',
1 => NULL,
),
5 =>
array (
0 => 'lynx, catamount',
1 =>
array (
0 =>
array (
0 => 'common lynx, Lynx lynx',
1 => NULL,
),
1 =>
array (
0 => 'Canada lynx, Lynx canadensis',
1 => NULL,
),
),
),
),
),
),
),
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment