Created
March 3, 2021 09:21
-
-
Save anhskohbo/87d53fa78a2c68b2dfd250267928c5fa to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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