I created a cache from xml, and by a construct I generate the object which finally become the arrays. And everything would be ok, if the key of these arrays wasnt "0". I dont know how it works. I searched the information how to change the class, or how to replace the keys. I am stuck. Could you help me with this.
$xml = simplexml_load_file($cache);
}
class Property {
public $xmlClass;
public $elemClass = '';
public $result_array = [];
public $data = '';
public function __construct($xml,$elem) {
$this->xmlClass=$xml;
$this->elemClass=$elem;
foreach($xml->list->movie as $value) {
$data = $value->$elem;
$this->result_array[] = $data;
}
}
public function getResult() {
return $this->result_array;
}
}
$result_zn = new Property($xml,'zn');
$result_au = new Property($xml,'au');
$result_ti = new Property($xml, 'ti');
$zn = $result_zn->getResult();
$au = $result_au->getResult();
$ti = $result_ti->getResult();
I think you can use the function array_values() to get the key 0,like this:
$arr = array(
'1' => 'cat',
'2' => 'dog'
);
$newarr = array_values($arr);
print_r($newarr);
and the result is :
Array ( [0] => cat [1] => dog )
Related
I have built a web scraper that recursively gets all URLs from a specific website and stores them in an array
Example below:
$array = [
'http://site.test',
'http://site.test/blog',
'http://site.test/blog/blog1',
'http://site.test/blog/blog2',
'http://site.test/services',
'http://site.test/services/service1',
'http://site.test/services/service2',
'http://site.test/services/service2/sub-service',
'http://site.test/product',
'http://site.test/product/product1',
'http://site.test/product/product1',
];
I am looking for some sort of way to organise this array into a multidimensional array so that I can see what pages are child pages and of which section something like the below structure
ie:
Home
----blog
--------article1
--------article2
----services
--------service1
--------service2
------------sub-service1
-----product
--------product1
--------product2
I have tried looping through and extracting certain segments of each string but cannot seem to get the desired result.
Ideally I would like to have the result in an array or even in displayed in a multi-level list for display purposes.
Any guidance would be much appreciated!
Let's try) we have an array of links
$array = [
'http://site.test',
'http://site.test/blog',
'http://site.test/blog/blog1',
'http://site.test/blog/blog2',
'http://site.test/services',
'http://site.test/services/service1',
'http://site.test/services/service2',
'http://site.test/services/service2/sub-service',
'http://site.test/product',
'http://site.test/product/product1',
'http://site.test/product/product2',
];
For creating a tree we should create the Node class
class Node
{
private array $childNodes;
private string $name;
public function __construct(string $name)
{
$this->name = $name;
$this->childNodes = [];
}
public function getName(): string
{
return $this->name;
}
public function addChildNode(Node $node): void
{
$this->childNodes[$node->getName()] = $node;
}
public function hasChildNode(string $name): bool
{
return array_key_exists($name, $this->childNodes);
}
public function getChildNode(string $name): Node
{
return $this->childNodes[$name];
}
public function getChildNodes(): array
{
return $this->childNodes;
}
}
And Tree class, that used Node class.
Method appendUrl parses URL and builds nodes chain.
class Tree
{
private Node $head;
public function __construct()
{
$this->head = new Node('Head');
}
public function getHead(): Node
{
return $this->head;
}
public function appendUrl(string $url): void
{
$parsedUrl = parse_url($url);
$uri = sprintf('%s//%s', $parsedUrl['scheme'], $parsedUrl['host']);
$keys = array_filter(explode('/', $parsedUrl['path'] ?? ''));
$keys = [$uri, ...$keys];
$node = $this->head;
foreach ($keys as $key) {
if (!$node->hasChildNode($key)) {
$prevNode = $node;
$node = new Node($key);
$prevNode->addChildNode($node);
} else {
$node = $node->getChildNode($key);
}
}
}
}
Now we create ConsoleTreeDrawer class that draw our tree to console
class ConsoleTreeDrawer
{
public function draw(Tree $tree): void
{
$node = $tree->getHead();
$this->drawNode($node);
}
private function drawNode(Node $node, int $level = 1): void
{
$prefix = implode('', array_fill(0, 2 * $level, '-'));
print("{$prefix}{$node->getName()}\n");
foreach ($node->getChildNodes() as $childNode) {
$this->drawNode($childNode, $level + 1);
}
}
}
And let`s use our classes
$tree = new Tree();
foreach ($array as $url) {
$tree->appendUrl($url);
}
$drawer = new ConsoleTreeDrawer();
$drawer->draw($tree);
And we drew the tree
--Head
----http//site.test
------blog
--------blog1
--------blog2
------services
--------service1
--------service2
----------sub-service
------product
--------product1
Algorithm:
Remove the prefix http:// for now as it is useless for our requirement. You can add it later on again.
Next is to sort all the elements using usort. Here, based on length obtained from exploding based on /.
Now, we can be assured that all parents are before child in the array.
Next is to assign version number/rank to each link. Naming is as follows:
'http://site.test' => 1
'http://site.test/blog' => 1.1
'http://site.test/services' => 1.2
'http://site.test/blog/blog1' => 1.1.1
Above is the strategy in which version numbers will be assigned.
Now, we just need to sort the array based on this version numbers using uasort
and you are done.
Snippet:
<?php
$array = [
'http://site.test',
'http://site.test/blog',
'http://site.test/blog/blog1',
'http://site.test/blog/blog2',
'http://site.test/services',
'http://site.test/services/service1',
'http://site.test/services/service2',
'http://site.test/services/service2/sub-service',
'http://site.test/product',
'http://site.test/product/product1',
];
// remove http://
foreach($array as &$val){
$val = substr($val,7);
}
// sort based on length on explode done on delimiter '/'
usort($array, function($a,$b){
return count(explode("/",$a)) <=> count(explode("/",$b));
});
$ranks = [];
$child_count = [];
// assign ranks/version numbers
foreach($array as $link){
$parent = getParent($link);
if(!isset($ranks[$parent])){
$ranks[$link] = 1;
}else{
$child_count[$parent]++;
$ranks[$link] = $ranks[$parent] . "." . $child_count[$parent];
}
$child_count[$link] = 0;
}
function getParent($link){
$link = explode("/",$link);
array_pop($link);
return implode("/",$link);
}
// sort based on version numbers
uasort($ranks,function($a,$b){
$version1 = explode(".", $a);
$version2 = explode(".", $b);
foreach($version1 as $index => $v_num){
if(!isset($version2[$index])) return 1;
$aa = intval($v_num);
$bb = intval($version2[$index]);
if($aa < $bb) return -1;
if($bb < $aa) return 1;
}
return count($version1) <=> count($version2);
});
// get the actual product links that were made as keys
$array = array_keys($ranks);
print_r($array);// now you can attach back http:// prefix if you like
Note: Current algorithm removes duplicates as well as there is no point in keeping them.
#Update:
Since you need a multidimensional hierarchical array, we can keep track of parent and child array link references and insert children into their respective parents.
<?php
$array = [
'http://site.test',
'http://site.test/blog',
'http://site.test/blog/blog1',
'http://site.test/blog/blog2',
'http://site.test/services',
'http://site.test/services/service1',
'http://site.test/services/service2',
'http://site.test/services/service2/sub-service',
'http://site.test/product',
'http://site.test/product/product1',
];
foreach($array as &$val){
$val = substr($val,7);
}
usort($array, function($a,$b){
return count(explode("/",$a)) <=> count(explode("/",$b));
});
$hier = [];
$set = [];
foreach($array as $link){
$parent = getParent($link);
if(!isset($set[$parent])){
$hier[$link] = [];
$set[$link] = &$hier[$link];
}else{
$parent_array = &$set[$parent];
$parent_array[$link] = [];
$set[$link] = &$parent_array[$link];
}
}
function getParent($link){
$link = explode("/",$link);
array_pop($link);
return implode("/",$link);
}
print_r($hier);
I need to access multiple arrays, the problem lies when I get to the arrays I need like below, I can't access it traditionally because the key will be different every time.
I'm dealing with the following array:
Array
(
[oe_schedule_charge] => Array
(
[617cdb2797153d6fbb03536d429a525b] => Array
(
[schedule] =>
[args] => Array
(
[0] => Array
(
[id] => cus_2OPctP95LW8smv
[amount] => 12
)
)
)
)
)
There are going to be hundreds of these arrays and I need a way to efficiently access the data within them. I'm using the following code with expected output:
function printValuesByKey($array, $key) {
if (!is_array($array)) return;
if (isset($array[$key]))
echo $key .': '. $array[$key] .'<br>';
else
foreach ($array as $v)
printValuesByKey($v, $key);
}
$cron = _get_cron_array();
foreach( $cron as $time => $hook ) {
if (array_key_exists('oe_schedule_charge', $hook)) {
echo '<div>';
echo date('D F d Y', $time);
echo printValuesByKey($hook, 'amount');
echo printValuesByKey($hook, 'id');
echo '</div>';
}
}
But I've never had to deal with this much data, so I would like to take the proper precautions. Any light that can be shed on accessing a multidimensional array like this in an efficient way would be greatly appreciated.
I would consider loading it into an object, then writing member functions to get what you want.
class myclass {
private $_uniqueKey;
private $_schedule;
private $_args = array();
private $_amount = array();
private $_id = array();
public function __construct($arrayThing)
{
foreach($arrayThing['oe_schedule_charge'] as $uniqueKey => $dataArray)
{
$this->_uniqueKey = $uniqueKey;
$this->_schedule = $dataArray['schedule'];
$this->_args = $dataArray['args'];
}
$this->_afterConstruct();
}
private function _afterConstruct()
{
foreach($this->_args as $argItem)
{
if(isset($argItem['amount']) && isset($argItem['id']))
{
$this->_amount[] = $argItem['amount'];
$this->_id[] = $argItem['id'];
}
}
}
public function getUniqueKey()
{
return $this->_uniqueKey;
}
public function getSchedule()
{
return $this->_schedule;
}
public function getArgs()
{
return $this->_args;
}
public function printShitOut($time)
{
//You define this. But if you do a print_r( on the object, it will tell you all the items you need. )
}
//code would be like this:
$cron = _get_cron_array();
foreach( $cron as $time => $hook )
{
$obj = new myclass($hook);
$obj->printShitOut($time);
}
How would something like this be possible:
I have an object called Player:
class Player
{
public $name;
public $lvl;
}
and I have an array of these players in: $array.
For example $array[4]->name = 'Bob';
I want to search $array for a player named "Bob".
Without knowing the array key, how would I search $array for a Player named "Bob" so that it returns the key #? For example it should return 4.
Would array_search() work in this case? How would it be formatted?
Using array_filter will return you a new array with only the matching keys.
$playerName = 'bob';
$bobs = array_filter($players, function($player) use ($playerName) {
return $player->name === $playerName;
});
According to php docs, array_search would indeed work:
$players = array(
'Mike',
'Chris',
'Steve',
'Bob'
);
var_dump(array_search('Bob', $players)); // Outputs 3 (0-index array)
-- Edit --
Sorry, read post to quick, didn't see you had an array of objects, you could do something like:
$playersScalar = array(
'Mike',
'Chris',
'Steve',
'Bob'
);
class Player
{
public $name;
public $lvl;
}
foreach ($playersScalar as $playerScaler) {
$playerObject = new Player;
$playerObject->name = $playerScaler;
$playerObjects[] = $playerObject;
}
function getPlayerKey(array $players, $playerName)
{
foreach ($players as $key => $player) {
if ($player->name === $playerName) {
return $key;
}
}
}
var_dump(getPlayerKey($playerObjects, 'Steve'));
I would like to create the following using class syntax:
$resp = new stdclass;
$resp->CategoryListResp->category[0]->categoryId = 1;
$resp->CategoryListResp->category[0]->categoryName = "Spel";
$resp->CategoryListResp->category[0]->iconUri = "PictoSpel.png";
$resp->CategoryListResp->category[1]->categoryId = 2;
$resp->CategoryListResp->category[1]->categoryName = "Transport";
$resp->CategoryListResp->category[1]->iconUri = "PictoTransport.png";
Should be easy but I cannot find the syntax for this.
I will later output $resp in json format. I am aware I can also use arrays for this...
The json output shall be:
{"CategoryListResp":{"category":[{"categoryId":1,"categoryName":"Spel","iconUri":"PictoSpel.png"},{"categoryId":2,"categoryName":"Transport","iconUri":"PictoTransport.png"}]}}
You can also make your classes more explicit:
class Category {
public $categoryId = 0, $categoryName = '', $iconUri = '';
}
class Resp {
public $categoryListResp = null;
public function __construct() {
$this->categoryListResp = new CategoryListResp();
}
}
class CategoryListResp {
public $category = array();
}
$resp = new Resp();
$resp->categoryListResp->category[0]->categoryId = 1;
$resp->categoryListResp->category[0]->categoryName = "Spel";
$resp->categoryListResp->category[0]->iconUri = "PictoSpel.png";
// etc.
ADDED (based on henq's comment). To fully utilize the class concept you would need to add some methods to the classes. Then you would not use -> for arrays, but call the respective methods. E.g.
class Category {
public $categoryId = 0, $categoryName = '', $iconUri = '';
public function __construct($id, $name, $icon) {
$this->categoryId = $id;
$this->categoryName = $name;
$this->iconUri = $icon;
}
}
class Resp {
public $categoryListResp = null;
public function __construct() {
$this->categoryListResp = new CategoryListResp();
}
public function addCategory($index, $id, $name, $icon) {
$this->categoryListResp->addCategory($index, $id, $name, $icon);
}
}
class CategoryListResp {
public $category = array();
public function addCategory($index, $id, $name, $icon) {
$this->category[$index] = new Category($id, $name, $icon);
}
}
$resp = new Resp();
$resp->addCategory(0, 1, "Spel", "PictoSpel.png");
$resp->addCategory(1, 2, "Transport", "PictoTransport.png");
// etc
You can modify this concept according to your needs.
You're almost there already:
$resp = new stdClass();
$resp->CategoryListResp = new stdClass();
$resp->CategoryListResp->category[0]->categoryId = 1;
$resp->CategoryListResp->category[0]->categoryName = "Spel";
$resp->CategoryListResp->category[0]->iconUri = "PictoSpel.png";
$resp->CategoryListResp->category[1]->categoryId = 2;
$resp->CategoryListResp->category[1]->categoryName = "Transport";
$resp->CategoryListResp->category[1]->iconUri = "PictoTransport.png";
print_r(json_encode($resp));
/*
output:
{"CategoryListResp":{"category":[{"categoryId":1,"categoryName":"Spel","iconUri":"PictoSpel.png"},{"categoryId":2,"categoryName":"Transport","iconUri":"PictoTransport.png"}]}}
*/
Just send $resp to json_encode. Your code should work as is, however. It's better design to create class definitions for CategoryListResp and Category, rather than just using stdClass.
Arrays are the simpler way to go (as suggested by #felix-kling)
This is how the code ended up:
$resp = array(
'CategoryListResp' => array(
'category' => array(
array(
'categoryId' => 1,
'categoryName' => 'Spel',
'iconUri' => 'PictoSpel.png'
),
array(
'categoryId' => 2,
'categoryName' => 'Transport',
'iconUri' => 'PictoTransport.png'
),
),
),
);
print json_encode($resp);
Clean and simple.
Is there a way to instantiate a new PHP object in a similar manner to those in jQuery? I'm talking about assigning a variable number of arguments when creating the object. For example, I know I could do something like:
...
//in my Class
__contruct($name, $height, $eye_colour, $car, $password) {
...
}
$p1 = new person("bob", "5'9", "Blue", "toyota", "password");
But I'd like to set only some of them maybe. So something like:
$p1 = new person({
name: "bob",
eyes: "blue"});
Which is more along the lines of how it is done in jQuery and other frameworks. Is this built in to PHP? Is there a way to do it? Or a reason I should avoid it?
the best method to do this is using an array:
class Sample
{
private $first = "default";
private $second = "default";
private $third = "default";
function __construct($params = array())
{
foreach($params as $key => $value)
{
if(isset($this->$key))
{
$this->$key = $value; //Update
}
}
}
}
And then construct with an array
$data = array(
'first' => "hello"
//Etc
);
$Object = new Sample($data);
class foo {
function __construct($args) {
foreach($args as $k => $v) $this->$k = $v;
echo $this->name;
}
}
new foo(array(
'name' => 'John'
));
The closest I could think of.
If you want to be more fancy and just want to allow certain keys, you can use __set() (only on php 5)
var $allowedKeys = array('name', 'age', 'hobby');
public function __set($k, $v) {
if(in_array($k, $this->allowedKeys)) {
$this->$k = $v;
}
}
get args won't work as PHP will see only one argument being passed.
public __contruct($options) {
$options = json_decode( $options );
....
// list of properties with ternary operator to set default values if not in $options
....
}
have a looksee at json_decode()
The closest I can think of is to use array() and extract().
...
//in your Class
__contruct($options = array()) {
// default values
$password = 'password';
$name = 'Untitled 1';
$eyes = '#353433';
// extract the options
extract ($options);
// stuff
...
}
And when creating it.
$p1 = new person(array(
'name' => "bob",
'eyes' => "blue"
));