Skip to content

Commit

Permalink
Add Random builder.
Browse files Browse the repository at this point in the history
  • Loading branch information
drupol committed Dec 12, 2019
1 parent 79fd15b commit ebbb505
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 2 deletions.
55 changes: 55 additions & 0 deletions spec/drupol/phptree/Builder/RandomSpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

declare(strict_types=1);

namespace spec\drupol\phptree\Builder;

use drupol\phptree\Builder\Random;
use drupol\phptree\Node\Node;
use drupol\phptree\Node\NodeInterface;
use PhpSpec\ObjectBehavior;

class RandomSpec extends ObjectBehavior
{
public function it_can_build_a_random_tree()
{
$parameters = [Node::class];
$nodes = [
'foo' => $parameters,
'bar' => $parameters,
];

$this::create($nodes)
->shouldReturn(null);

$nodes = array_values($nodes);

$this::create($nodes)
->shouldBeAnInstanceOf(NodeInterface::class);

$this::create($nodes)
->count()
->shouldReturn(1);

$parameters = [static function () {
return Node::class;
}];

$nodes = [
$parameters,
$parameters,
];

$this::create($nodes)
->shouldBeAnInstanceOf(NodeInterface::class);

$nodes = array_pad([], 20, $parameters);
$this::create($nodes)
->shouldBeAnInstanceOf(NodeInterface::class);
}

public function it_is_initializable()
{
$this->shouldHaveType(Random::class);
}
}
4 changes: 2 additions & 2 deletions src/Builder/BuilderInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ interface BuilderInterface
/**
* @param iterable<NodeInterface> $nodes
*
* @return \drupol\phptree\Node\NodeInterface
* @return \drupol\phptree\Node\NodeInterface|null
*/
public static function create(iterable $nodes): NodeInterface;
public static function create(iterable $nodes): ?NodeInterface;
}
81 changes: 81 additions & 0 deletions src/Builder/Random.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php

declare(strict_types=1);

namespace drupol\phptree\Builder;

use drupol\phptree\Node\NodeInterface;
use function is_callable;

/**
* Class Random.
*/
class Random implements BuilderInterface
{
/**
* {@inheritdoc}
*/
public static function create(iterable $nodes): ?NodeInterface
{
$root = null;

foreach ($nodes as $key => $value) {
if (0 === $key) {
$root = self::createNode($value);

continue;
}

if (false === ($root instanceof NodeInterface)) {
continue;
}

self::pickRandomNode($root)->add(self::createNode($value));
}

return $root;
}

/**
* @param array<mixed> $parameters
*
* @return \drupol\phptree\Node\NodeInterface
*/
private static function createNode(array $parameters = []): NodeInterface
{
$parameters = array_map(
static function ($parameter) {
if (is_callable($parameter)) {
return $parameter();
}

return $parameter;
},
$parameters
);

$class = array_shift($parameters);

return new $class(...$parameters);
}

/**
* @param \drupol\phptree\Node\NodeInterface $node
*
* @return \drupol\phptree\Node\NodeInterface
*/
private static function pickRandomNode(NodeInterface $node): NodeInterface
{
$pick = (int) random_int(0, $node->count());

$i = 0;

foreach ($node->all() as $child) {
if (++$i === $pick) {
return $child;
}
}

return $node;
}
}

0 comments on commit ebbb505

Please sign in to comment.