The following is a sample array:
array(11, -2, 4, 35, 0, 8, -9)
I would like to use oop to sort it and generate this result:
Output :
Array ( [0] => -9 [1] => -2 [2] => 0 [3] => 4 [4] => 8 [5] => 11 [6] => 35 )
I was provided the solution far below, which works. What I don't understand is what the __construct is doing. I have a beginner's understanding of how constructors work, but what specifically is the purpose of this constructor?:
public function __construct(array $asort)
{
$this->_asort = $asort;
Is it turning the input into an array?
<?php
class array_sort
{
protected $_asort;
public function __construct(array $asort)
{
$this->_asort = $asort;
}
public function alhsort()
{
$sorted = $this->_asort;
sort($sorted);
return $sorted;
}
}
$sortarray = new array_sort(array(11, -2, 4, 35, 0, 8, -9));
print_r($sortarray->alhsort());
?>
It's a reference to the current object, it's most commonly used in object oriented code.
Here is a reference and a longer primer.
The array $asort is assigned to the variable $_asort of the class and take $_asort as a property of the object created.
The __construct() is initialising the protected $_asort;. Essentially moving your array parameter into a class variable, which is then utilised by alhsort().
For clarification, the Input as you said is already an array once you declare array(...) the construct simple copies it into a variable accessible by the entire array_sort class.
This is way too over engineered!
Why do you want to do this oop when you can simply call
$sortarray = sort(array(11, -2, 4, 35, 0, 8, -9));
To answer your question, __construct() is called whenever the object is instantiated new array_sort(array(11, -2)); instantiates the array_sort object.
Your particular constructor is setting the protected property $_asort with the value passed into the first argument $asort.
<?php
class array_sort
{
public $temp;
public function alhsort(array $sorted)
{
for($i=0; $i<count($sorted)-1; $i++){
for($j=$i+1; $j<count($sorted); $j++){
if($sorted[$i] > $sorted[$j]){
$temp = $sorted[$i];
$sorted[$i] = $sorted[$j];
$sorted[$j] = $temp;
}
}
}
return $sorted;
}
}
$sortarray = new array_sort();
print_r($sortarray->alhsort(array(11, -2, 4, 35, 0, 8, -9)));
?>
Related
Given that I'm getting different object results depending of the API I query, here are two examples out of 35 I have to implement:
stdClass Object
(
[mid] => 7127.75
[bid] => 7126.6
[ask] => 7128.9
[last_price] => 7128.8
[low] => 7000.0
[high] => 7492.1
[volume] => 53255.4195502
[timestamp] => 1510265647.9803913
)
stdClass Object
(
[Success] => 1
[Message] =>
[Data] => stdClass Object
(
[AskPrice] => 7095
[BidPrice] => 7070
[Low] => 7001
[High] => 7540
[Volume] => 17.38943459
[LastPrice] => 7090
[Change] => -1.02
[Open] => 7163
[Close] => 7090
)
[Error] =>
)
I want to build mapping variable array to access the object easily.
$xmap["a"]["bid"] = "bid";
$xmap["b"]["bid"] = "Data->BidPrice";
Let's assume $content has the first example object, this will work:
print $content->{$xmap["a"]["bid"]}; // the result is 7128.9
As for the second example object, it does not:
print $content->{$xmap["b"]["bid"]}; // the result is PHP Notice: Undefined property: stdClass::$Data->BidPrice in ...
Can this be done or am I stuck with if statements!
First, convert all the objects into assoc. arrays by using json_encode/decode. You'll find this code multiple times here in stackoverflow
$arr = json_decode(json_encode($obj), true);
Second, I recommend a dot-notation for the key-path, plus a tiny function to find the value in a multidimensional array.
Example:
function fromArray($key, $arr) {
$keys = explode('.', $key);
foreach($keys as $k) {
if (!isset($arr[$k]))
return array(); // return empty array
$arr = $arr[$k];
}
return $arr; // can be a scalar value or array
}
$key = 'my.super.array.content';
$array = ['my' => [
'super' => [
'array' =>[
'content'=>4711
]
]
]
];
var_dump($array, fromArray($key, $array));
/*
array(1) {
["my"]=>
array(1) {
["super"]=>
array(1) {
["array"]=>
array(1) {
["content"]=>
int(4711)
}
}
}
}
int(4711)
*/
I found the dot-notation very useful when dealing with complex structures.
You can convert your objects to arrays and build a large mapping array that needs to be maintained and then explode it and loop through it to access the other arrays; or you can try using patterns. I'm thinking Adapter, but maybe another is a better fit. This is using your second object as an example, but just add as many as needed:
class ContentAdapter {
public function __get($name) {
return $this->obj->{$xmap[$name]};
}
}
class ContentAdapter_API_B extends ContentAdapter {
public $xmap = ['bid' => 'BidPrice', 'ask' => 'AskPrice'];
public function __construct($obj) {
$this->obj = $obj->data;
}
}
Now it is consistant regardless of the object since each has an adapter:
$content = new ContentAdapter_API_B($content);
echo $content->bid;
Using your first object you can either create a child as well (ContentAdapter_API_A in case the structure ever changes) or instantiate directly:
$content = new ContentAdapter($content);
echo $content->bid;
Or obviously just use it as is:
echo $content->bid;
An alternate way without inheritance is to use getters:
class ContentAdapter_API_B {
public function __construct($obj) {
$this->obj = $obj->data;
}
public function getBid() { return $this->obj->BidPrice; }
}
So long as the methods are consistent then it will always work:
$content = new ContentAdapter_API_B($content);
echo $content->getBid;
I was reading a WordPress tutorial in which the author used something like this (I simplified it):
class WPObject {
public $ID;
public $title;
public $content;
public $status;
public function __construct($wp_post) {
$modifiers = [
'key' => function($k, $v) {
return (substr($k, 0, 5) === "post_") ? substr($k, 5) : $k;
}
];
}
}
The function is supposed to remove the post_ prefix from the wp queried object. The question I have is regarding the function I posted above. That anonymous function seems to return an object with with the properties. When I did a print_r on it I get...
Array
(
[key] => Closure Object
(
[this] => WPObject Object
(
[ID] =>
[title] =>
[content] =>
[status] =>
)
[parameter] => Array
(
[$k] =>
[$v] =>
)
)
)
I'm still learning about anonymous functions and was wondering how/why it does this? If you call an anonymous function from an object, does it create an instance of that object or something?
Also, sorry if I'm using incorrect terminology. Don't have anonymous functions, closures, lambda functions straightened out yet.
Not a new instance, it has a reference to the same object in which it is created since PHP 5.4 I believe. So the closure itself can call properties or methods on that class as if being in that class.
class foo {
public $bar = 'something';
function getClosure(){
return function(){
var_dump($this->bar);
};
}
}
$object = new foo();
$closure = $object->getClosure();
//let's inspect the object
var_dump($object);
//class foo#1 (1) {
// public $bar =>
// string(9) "something"
//}
//let's see what ->bar is
$closure();
//string(9) "something"
//let's change it to something else
$object->bar = 'somethingElse';
//closure clearly has the same object:
$closure();
//string(13) "somethingElse"
unset($object);
//no such object/variables anymore
var_dump($object);
//NULL (with a notice)
//but closure stills knows it as it has a reference
$closure();
//string(13) "somethingElse"
I have a method in a class that looks like this;
class SomeClass {
private $hidden = array(....);
/**
* #return array - numeric indexed array in order of $this->hidden.
* Suitable for use by list(var1, var2, ...)
*/
public function getAsList($list = array())
{
return array_values(array_intersect_key($this->hidden, array_flip($list) );
}
But this is not useful, since the caller of the method does not know the order of the key/element pairs in the associative array in instance variable $hidden. Ideally, the returned array would be in the exact same order as the keys specified in $list. For example:
$foo = new SomeClass();
list($foo, $bar, $baz) = $foo->getAsList(array('foo', 'bar', 'baz');
I can easily write some explicit, verbose PHP code in a loop to do this, but is there some clever way to use the various array functions, e.g. array_multisort() to spit this out in minimal lines of code (and hopefully, at compiled code speed -- I'll test it, if it matters).
In a sense, this is a brain teaser to which I don't yet know the answer. It's not critical I do it without an explicit loop, but I'm curious as to if it can be done. I spent 30 or so minutes on it, and haven't found a solution yet.
Perhaps array_replace is the missing piece to your puzzle:
public function getAsList($list = array())
{
$klist = array_flip($list);
return array_values(array_intersect_key(array_replace($klist, $this->hidden), $klist));
}
Example (Demo):
$hidden = [
'apples' => 19,
'eggs' => 7,
'grapes' => 144,
'mushrooms' => 3,
'oranges' => 16
];
$list = ['grapes', 'apples', 'eggs', 'oranges'];
$klist = array_flip($list);
print_r(array_values(array_intersect_key(array_replace($klist, $hidden), $klist)));
/*
Array
(
[0] => 144
[1] => 19
[2] => 7
[3] => 16
)
*/
This is one of those cases when functional programming pales in comparison to a language construct in terms of readability, maintanability, and efficiency.
I have a bias toward functional programming, but in this case it just doesn't pay.
Code: (Demo)
$hidden = [
'apples' => 19,
'eggs' => 7,
'grapes' => 144,
'mushrooms' => 3,
'oranges' => 16
];
$list = ['grapes', 'apples', 'eggs', 'oranges'];
foreach ($list as $item) {
if (isset($hidden[$item])) {
$result[] = $hidden[$item];
}
}
var_export($result);
Output:
array (
0 => 144,
1 => 19,
2 => 7,
3 => 16,
)
If you insist on using functional programming, then it would be most efficient to perform the required operations in this order:
filter the array
order the keys of the filtered array
reindex the ordered, filtered array
Here is how:
Code: (Demo)
$flippedList = array_flip($list);
var_export(array_values(array_replace($flippedList, array_intersect_key($hidden, $flippedList))));
(same output as previous snippet)
Logically, it doesn't make sense to order an array that has excess elements in it. Lighten the load, first.
I am having a problem accessing an object in a multidimensional array.
THE CONTEXT
Basically, I have an object (category) which consists of Name, ID, ParentID and more. I also have an array ultimateArray which is multidimentional.
For a given category, I am writing a function (getPath()) that will return an array of ids. For example, an object named Granny Smith has a parentID of 406 and is therefore a child of Food(5) -> Fruits(101) -> Apples(406). The function will return either an array or string of the ids of the objects parents. In the above example this would be: 5 -> 101 -> 406 or ["5"]["101"]["406"] or [5][101][406]. Food is a root category!
THE PROBLEM
What I need to do is use whatever is returned from getPath() to access the category id 406 (Apples) so that I can add the object Granny Smith to the children of Apples.
The function $path = $this->getPath('406'); is adaptable. I am just having difficulty using what is returned in the following line:
$this->ultimate[$path]['Children'][]= $category;
It works when I hard code in:
$this->ultimate["5"]["101"]["406"]['Children'][]= $category;
//or
$this->ultimate[5][101][406]['Children'][]= $category;
Any help is much appreciated.
Suppose you have the array like below
<?php
$a = array(
12 => array(
65 => array(
90 => array(
'Children' => array()
)
)
)
);
$param = array(12, 65, 90); // your function should return values like this
$x =& $a; //we referencing / aliasing variable a to x
foreach($param as $p){
$x =& $x[$p]; //we step by step going into it
}
$x['Children'] = 'asdasdasdasdas';
print_r($a);
?>`
You can try referencing or aliasing it
http://www.php.net/manual/en/language.references.whatdo.php
The idea is to make a variable which is an alias of your array and going deep from the variable since we can't directly assigning multidimensional key from string (AFAIK)
output
Array
(
[12] => Array
(
[65] => Array
(
[90] => Array
(
[Children] => asdasdasdasdas
)
)
)
)
You can use a recursive function to access the members. This returns NULL if the keys do not correspond with the path, but you could also throw errors or exceptions there. Also please note that i have added "Children" to the path. I have done this so you can use this generically. I just did an edit to show you how to do it without children in the path.
<?php
$array = array(1 => array(2 => array(3 => array("Children" => array("this", "are", "my", "children")))));
$path = array(1, 2, 3, "Children");
$pathWithoutChildren = array(1, 2, 3);
function getMultiArrayValueByPath($array, $path) {
$key = array_shift($path);
if (array_key_exists($key, $array) == false) {
// requested key does not exist, in this example, just return null
return null;
}
if (count($path) > 0) {
return getMultiArrayValueByPath($array[$key], $path);
}
else {
return $array[$key];
}
}
var_dump(getMultiArrayValueByPath($array, $path));
$results = getMultiArrayValueByPath($array, $pathWithoutChildren);
var_dump($results['Children']);
Say hypothetically I have a class...
class Main {
$prop1 = 2;
$prop2 = 23;
...
$prop42 = "what";
function __construct($arg_array) {
foreach ($arg_array as $key => $val) {
$this->$key = $val;
}
}
}
Say I create and object...
$attributes = array("prop1"=>1, "prop2"=>35235, "prop3"=>"test");
$o = new Main($attributes);
Providing for default property values if not supplied by the user is obvious. But what if I want to enforce arbitrary limits on user supplied values for object properties? What if I want to enforce $prop1 to be of int, be no less than 1, and be no greater than 5. And, $prop42 to be of type string, no less than 'A', and no greater than 'Z'? For this purpose, what would be the cleanest way, keeping the script as short and sweet as possible, using any possible language feature or trick?
I'm stuck in __construct() checking supplied values against a rule array built like so...
$allowable = array(
"prop1" => array(
'type' => 'int',
'allowable_values' => array(
'min' => 1,
'max' => 5
)
),
"prop2" => array(
'type' => 'int',
'allowable_values' => array(
1,
235,
37,
392,
13,
409,
3216
)
),
...
"prop42" => array(
'type' => 'string',
'allowable_values' => array(
'min' => 'A',
'max' => 'Z'
)
)
);
As you can see by prop2, my validation function is starting to get pretty messy with so many 'if-then-iterate-again' blocks as I have to account for not only ranges but a list of permitted values. With the validation code and this rule array, my script is getting rather bulky.
The question is, how can I structure my class or class properties or the validation code or any other aspect of my script to be as short and concise as possible to allow property range and value enforcement? Is there a language feature or trick to handle this more elegantly? Have I reached a brick wall, the limit of this language? Are there any examples from other languages that can easily implement this which can provide some clue?
getters and setters
class Main {
private $prop1;
private $prop2;
private $prop3;
public function __construct( $p1 , $p2 , $p3 )
{
$this->setProp1($p1);
$this->setProp2($p2);
$this->setProp3($p3);
}
public function setProp1($p1)
{
// conditional checking for prop1
if(!$ok) throw new Exception('problem with prop1');
$this->prop1 = $p1;
}
//.. and so on
}
I ran into a similar issue the other day. Here's what I would do:
private $props;
private $rules;
function __construct($params) {
// or you can get the rules from another file,
// or a singleton as I suggested
$this->rules = array (array('type' => 'range', 'min' => 10, 'max' => 20),
array('type' => 'in_set', 'allowed' => array(1,2,3)));
for ($i=0; $i<count($params); $i++) {
if ($this->check($params[$i], $this->rules($i))
$this->props[$i] = $params[$i];
else
throw new Exception('Error adding prop ' . $i);
}
}
function check($value, $rule) {
switch($rule['type']) {
case 'range':
return ($rule['min'] <= $value && $value <= $rule['max']);
case 'in_set':
return (in_array($value, $rule['allowed']));
// and so on
}
}
If you have many parameters, you can use an array and iterate through that.
If your validation rules are always going to be the same, you can put them in a separate file and load that in your constructor or whatever.
EDIT: By the way, there is really no point in testing type in PHP. It is both not very reliable and unnecessary.
EDIT 2: Instead of having a global variable with the rules, you can use a Singleton: