Skip to content

Instantly share code, notes, and snippets.

@anhskohbo
Created March 3, 2021 09:21
Show Gist options
  • Save anhskohbo/87d53fa78a2c68b2dfd250267928c5fa to your computer and use it in GitHub Desktop.
Save anhskohbo/87d53fa78a2c68b2dfd250267928c5fa to your computer and use it in GitHub Desktop.
<?php
abstract class Node implements JsonSerializable
{
/**
* @var string
*/
public string $id;
/**
* @var Node|null
*/
protected ?Node $parent;
/**
* @var Node[]
*/
protected array $children = [];
/**
* @var array
*/
protected array $attributes = [];
/**
* Node constructor.
*
* @param string $id
* @param array $attributes
*/
public function __construct(string $id, array $attributes)
{
$this->id = $id;
$this->attributes = $attributes;
}
/**
* Adds the given node to this node's children.
*
* @param Node $child
*/
public function addChild(Node $child): void
{
$this->children[] = $child;
$child->parent = $this;
$child->attributes['parentId'] = $this->id;
}
/**
* @return int Tree level (1 = top level)
*/
public function getLevel(): int
{
if (null === $this->parent) {
return 0;
}
return $this->parent->getLevel() + 1;
}
/**
* @return Node|null
*/
public function getParent(): ?Node
{
return $this->parent ?? null;
}
/**
* @return Node[]
*/
public function getChildren(): array
{
return $this->children;
}
/**
* @return array
*/
public function toArray(): array
{
$attributes = $this->attributes;
$attributes['children'] = array_map(
static function ($child) {
return $child->toArray();
},
$this->children
);
return $attributes;
}
/**
* {@inheritdoc}
*/
public function jsonSerialize(): array
{
return $this->toArray();
}
}
class Company extends Node
{
/**
* @var array|array[]
*/
protected array $travelData = [];
/**
* @var array
*/
protected static array $costs = [];
/**
* @param array|array[] $travelData
*/
public function setTravelData(array $travelData): void
{
$this->travelData = $travelData;
}
/**
* @return float|int
*/
public function getTravelCost()
{
$prices = array_column($this->travelData, 'price');
return array_sum($prices);
}
/**
* @return float|int
*/
public function getTotalTravelCost()
{
if (isset(static::$costs[$this->id])) {
return static::$costs[$this->id];
}
return static::$costs[$this->id] = array_reduce(
$this->getChildren(),
static function ($cost, $child) {
return $cost + $child->getTotalTravelCost();
},
$this->getTravelCost()
);
}
/**
* {@inheritdoc}
*/
public function toArray(): array
{
$array = parent::toArray();
return [
'id' => $array['id'],
'name' => $array['name'],
'cost' => $this->getTotalTravelCost(),
'children' => $array['children'],
];
}
public static function getData(): array
{
$data = json_decode(
file_get_contents('https://5f27781bf5d27e001612e057.mockapi.io/webprovise/companies'),
true,
512,
JSON_THROW_ON_ERROR
);
return $data ?? [];
}
}
class CompanyTree
{
/**
* @var array|Node[]
*/
protected array $nodes;
/**
* @var array|array[]
*/
protected array $travelDataList;
/**
* Tree constructor.
*
* @param array $elements
* @param array $travelDataList
*/
public function __construct(array $elements, array $travelDataList)
{
$this->travelDataList = Travel::groupData($travelDataList);
$this->buildNodes($elements);
}
/**
* @return Node|null
*/
public function getRootNode(): ?Node
{
return $this->getRootNodes()[0] ?? null;
}
/**
* @return Node[]
*/
public function getRootNodes(): array
{
return $this->nodes['0']->getChildren();
}
/**
* @param array $elements
* @param string $rootId
* @return void
*/
private function buildNodes(array $elements, $rootId = '0'): void
{
// Create root node.
$this->nodes[$rootId] = new Company($rootId, []);
// Hold the children id of nodes.
$children = [];
foreach ($elements as $element) {
$id = $element['id'];
$this->nodes[$id] = new Company($id, $element);
if (isset($this->travelDataList[$id])) {
$this->nodes[$id]->setTravelData($this->travelDataList[$id]);
}
if (empty($children[$element['parentId']])) {
$children[$element['parentId']] = [$id];
} else {
$children[$element['parentId']][] = $id;
}
}
foreach ($children as $parentId => $childIds) {
foreach ($childIds as $id) {
if (isset($this->nodes[$parentId]) && $this->nodes[$parentId] !== $this->nodes[$id]) {
$this->nodes[$parentId]->addChild($this->nodes[$id]);
}
}
}
}
}
class Travel
{
public static function getData(): array
{
$data = json_decode(
file_get_contents('https://5f27781bf5d27e001612e057.mockapi.io/webprovise/travels'),
true,
512,
JSON_THROW_ON_ERROR
);
return $data ?? [];
}
public static function groupData(array $elements): array
{
$data = [];
foreach ($elements as $element) {
if (!isset($data[$element['companyId']])) {
$data[$element['companyId']] = [$element['id'] => $element];
} else {
$data[$element['companyId']][$element['id']] = $element;
}
}
return $data;
}
}
class TestScript
{
public function execute(): void
{
$start = microtime(true);
$travelData = Travel::getData();
$tree = new CompanyTree(Company::getData(), $travelData);
// dump($tree->getRootNode()->toArray());
echo '<pre>' . print_r($tree->getRootNode()->toArray(), true) . '</pre>';
// Enter your code here
echo 'Total time: ' . (microtime(true) - $start);
}
}
(new TestScript())->execute();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment