The result of array_unique - php

Working with DateTime in projects again have a problem with duplicating if use array_unique to array which have a elemts of object,(but probles only with DateTime), see code:
class simpleClass
{
public $dt;
function __construct($dt)
{
$this->dt = $dt;
}
}
$dateObj = new simpleClass(new DateTime);
$std = new stdClass;
$arr = [$dateObj, $dateObj, $std, $std, $std, $std];
var_dump(array_unique($arr, SORT_REGULAR));
Expected 1 element with dateObj
But actually there 2

Function array_unique() will compare strings, so objects will be casted to strings. Solution to that would be to use __toString() magic method to return full date identifier:
class simpleClass
{
public $dt;
function __construct(DateTime $dt) {
$this->dt = $dt;
}
public function __toString() {
return $this->dt->format('r');
}
}
$dateObj1 = new simpleClass(new DateTime);
$dateObj2 = new simpleClass(new DateTime);
$dateObj3 = new simpleClass(new DateTime('today'));
$arr = [$dateObj1, $dateObj2, $dateObj3];
print_r(array_unique($arr));
Demo.

I still can't understand. Setting the array with:
$arr = [$dateObj, $dateObj, $std, $std];
returns:
array (size=2)
0 =>
object(simpleClass)[1]
public 'dt' =>
object(DateTime)[2]
public 'date' => string '2013-11-14 14:37:08' (length=19)
public 'timezone_type' => int 3
public 'timezone' => string 'Europe/Rome' (length=11)
2 =>
object(stdClass)[3]
This way, array_unique seems to work...

Related

How to get the difference between two arrays of objects

I have 2 array of objects, for example
$arr1 = [
(new MyClass())->setId(1),
(new MyClass())->setId(2),
(new MyClass())->setId(3),
];
$arr2 = [
(new MyClass())->setId(1),
(new MyClass())->setId(2),
];
I'd need to find the difference between these 2 arrays, in this example I'd need to get an array with a single element, the one with id == 3.
I know I should use array_udiff (maybe) but I couldn't figure out how.
The following logic might help:
<?php
class MyClass
{
public $id = null;
public function setId($id) {
$this->id = $id;
return $this;
}
}
$arr1 = [
(new MyClass())->setId(1),
(new MyClass())->setId(2),
(new MyClass())->setId(3),
];
$arr2 = [
(new MyClass())->setId(1),
(new MyClass())->setId(2),
];
$difference = array_udiff($arr1, $arr2,
function ($objOne, $objTwo) {
return $objOne->id - $objTwo->id;
}
);
Output:
Array
(
[2] => MyClass Object
(
[id] => 3
)
)
working demo

Recursive function return issue

I'm writing a recursive function like below:
private function getManager($employee)
{
$manager = $employee->manager;
if ($manager) {
array_push($this->managers, $manager->id);
$this->getManager($manager);
}
return;
}
This function receive an employee and find his manage. If find a manage, then push manager id into an array ($this->managers on line 5). Then call this function recursively and pass manager as an employee. If no manager found on line 3, then this function just return (line 8).
So my question is, is their any problem if i'm not return the recursive call at line 6 ($this->getManager($manager);)
Not sure if this is what you think, but it works.
function getManagers($employee)
{
$managers = [];
if (isset($employee->manager)) {
array_push($managers, $employee->manager->id);
array_push($managers, ...getManagers($employee->manager));
}
return $managers;
}
No, there is absolutely no benefit in writing the empty return. The method will halt regardless of the existence of the return.
Please observe the two methods below which show identical, error-less outcomes regardless of the return.
Code: (Demo)
class Management
{
private $managers = [];
function __construct($managerTree)
{
$this->getManager($managerTree);
var_export($this->managers);
echo "\n---\n";
$this->managers = [];
var_export($this->managers);
echo "\n---\n";
$this->getManager2($managerTree);
var_export($this->managers);
}
private function getManager(?object $employee): void
{
$manager = $employee->manager;
if ($manager) {
array_push($this->managers, $manager->id);
$this->getManager($manager);
}
return;
}
private function getManager2(?object $employee): void
{
$manager = $employee->manager;
if ($manager) {
array_push($this->managers, $manager->id);
$this->getManager($manager);
}
}
}
new Management(
(object) [
'id' => 3,
'manager' => (object) [
'id' => 2,
'manager' => (object) [
'id' => 1,
'manager' => null
]
]
]
);
Output:
array (
0 => 2,
1 => 1,
)
---
array (
)
---
array (
0 => 2,
1 => 1,
)

How do I properly construct an array within a class in PHP?

Currently I'm working on a simple OOP script in PHP, it needs to compare the ID and the DATE of the array and sort them in the right order.
I was wondering why my constructor in the first class doesn't pass the $elements array properly.
The error I'm getting:
Notice: Undefined variable: elements in /Applications/XAMPP/xamppfiles/htdocs/strategy-pattern-.php on line 58
Catchable fatal error: Argument 1 passed to ObjectCollection::__construct() must be of the type array, null given, called in ... on line 58 and defined in ... on line 12
Code:
<?php
class ObjectCollection
{
var $elements = array(
array('id' => 2, 'date' => '2017-01-01',),
array('id' => 1, 'date' => '2017-02-01'));
var $comparator;
function __construct(array $elements)
{
$this->elements = $elements;
}
function sort()
{
if (!$this->comparator) {
throw new \LogicException('Comparator is not set');
}
uasort($this->elements, [$this->comparator, 'compare']);
return $this->elements;
}
function setComparator(ComparatorInterface $comparator)
{
$this->comparator = $comparator;
}
}
interface ComparatorInterface
{
function compare($a, $b);
}
class DateComparator implements ComparatorInterface
{
function compare($a, $b)
{
$aDate = new \DateTime($a['date']);
$bDate = new \DateTime($b['date']);
return $aDate <> $bDate;
}
}
class IdComparator implements ComparatorInterface
{
function compare($a, $b)
{
return $a['id'] <> $b['id'];
}
}
$collection = new ObjectCollection($elements);
$collection->setComparator(new IdComparator());
$collection->sort();
echo "Sorted by ID:\n <br>";
print_r($collection->elements);
$collection->setComparator(new DateComparator());
$collection->sort();
echo "<br>Sorted by date:\n <br>";
print_r($collection->elements);
?>
I know there may just be a rookie mistake somewhere but I'm really curious what I'm doing wrong haha.
Thanks in advance! :)
At the bottom of your script you have:
$collection = new ObjectCollection($elements);
However, the $elements variable is not defined. This is why you are getting the error.
The specific error is related to the fact that you used a type declaration in your class constructor requiring that an 'array' be passed. Prior to the addition of type declarations to php, the php runtime engine did not care what variables you passed, so long as you passed a number of variables equal to the number of required parameters to a function or method.
As also pointed out in another answer, many of us are assuming that your placement of the --
var $elements = array(
array('id' => 2, 'date' => '2017-01-01',),
array('id' => 1, 'date' => '2017-02-01'));
was never meant to be inside the class. With that said, doing so creates and initializes the $elements class variable, which is a valid technique that has many uses in OOP. However, the syntax used is obsolete, and if you really did want to initialize a class variable to a set value at object creation time, you should be using the syntax that includes a variable visibility keyword like:
protected $elements = array(
array('id' => 2, 'date' => '2017-01-01',),
array('id' => 1, 'date' => '2017-02-01'));
In conclusion, the answer to your question is that either you should define $collection to be an array at the bottom of the script, or pass an array in when you create the ObjectCollection object.
$collection = new ObjectCollection(array(
array('id' => 2, 'date' => '2017-01-01'),
array('id' => 1, 'date' => '2017-02-01'));
class ObjectCollection
{
// define as property
private $elements;
private $comparator;
function __construct(array $elements)
{
$this->elements = $elements;
}
function sort()
{
if (!$this->comparator) {
throw new \LogicException('Comparator is not set');
}
uasort($this->elements, [$this->comparator, 'compare']);
return $this->elements;
}
function setComparator(ComparatorInterface $comparator)
{
$this->comparator = $comparator;
}
}
...
// you need to define $elements to pass
$elements = array(
array('id' => 2, 'date' => '2017-01-01',),
array('id' => 1, 'date' => '2017-02-01'));
// them to the constructor
$collection = new ObjectCollection($elements);
// the way you did it, your $elements definition was in class scope so you got the error they are "NULL" / Not defined
You have declared the elements variable inside the class instead outside
$elements = array(
array('id' => 2, 'date' => '2017-01-01',),
array('id' => 1, 'date' => '2017-02-01'));
class ObjectCollection
{

Select smallest and biggest value of an object

I'm trying to get the smallest value of start and the biggest value of end outoff the object below. The object is an result of a sql script.
object(CI_DB_mysql_result)[18]
public 'conn_id' => resource(54, mysql link persistent)
public 'result_id' => resource(61, mysql result)
public 'result_array' =>
array (size=0)
empty
public 'result_object' =>
array (size=3)
0 =>
object(stdClass)[16]
public 'id' => string '601' (length=3)
public 'scheduled' => string '0' (length=1)
public 'start' => string '2014-10-17 06:00:00' (length=19)
public 'end' => string '2014-10-17 11:00:00' (length=19)
1 =>
object(stdClass)[19]
public 'id' => string '602' (length=3)
public 'scheduled' => string '0' (length=1)
public 'start' => string '2014-10-17 18:00:00' (length=19)
public 'end' => string '2014-10-17 19:30:00' (length=19)
2 =>
object(stdClass)[20]
public 'id' => string '603' (length=3)
public 'scheduled' => string '1' (length=1)
public 'start' => string '2014-10-17 11:00:00' (length=19)
public 'end' => string '2014-10-17 18:00:00' (length=19)
public 'custom_result_object' =>
array (size=0)
empty
public 'current_row' => int 0
public 'num_rows' => int 3
public 'row_data' => null
In this case the smallest value of start = '2014-10-17 06:00:00' and the biggest value of end = '2014-10-17 19:30:00'. So the result I'm looking for looks like this:
$smallest_start_value = '2014-10-17 06:00:00';
$biggest_end_value = '2014-10-17 19:30:00';
How should the code look like, in order to compute this result?
Function:
function unschedule_resource() {
$event_id = 4;//$_POST['event_id'];
$event_start = '2014-10-17 11:00:00';//$_POST['event_start'];
$event_end = '2014-10-17 18:00:00';//$_POST['event_end'];
// Get resource id
$this->db->select('
event.resource_id'
);
$this->db->from('promo_manager.event');
$this->db->where('event.id =', $event_id);
$data = $this->db->get();
foreach ($data->result() as $row) {
echo $row->resource_id . "<br>";
$resource_id = $row->resource_id;
}
// Get resource events to unschedule
$this->db->select('
resource_calendar.id,
resource_calendar.scheduled,
resource_calendar.start,
resource_calendar.end'
);
$this->db->from('promo_manager.resource_calendar');
$this->db->where('resource_calendar.resource_id =', $resource_id);
$this->db->where('resource_calendar.start =', $event_start);
$this->db->where('resource_calendar.end =', $event_end);
$this->db->or_where('resource_calendar.end =', $event_start);
$this->db->or_where('resource_calendar.start =', $event_end);
$data = $this->db->get();
$lowest_value = null;
$highest_value = null;
foreach ($data as $the_key => $the_value) {
if ($the_key == 'start') {
if ($lowest_value === null) {
$lowest_value = $the_value;
}
if ($the_value < $lowest_value) {
$lowest_value = $the_value;
}
}
}
//echo $lowest_value;
var_dump ($lowest_value) ;
}
The code of Sam should look like this:
$lowest_value = null;
$highest_value = null;
foreach($data as $obj) { //$data should be the array containing the result objects.
$start = $obj->start;
$end = $obj->end;
if ($lowest_value === null)
$lowest_value = $start;
else
if ($lowest_value > $start)
$lowest_value = $start;
if ($highest_value === null)
$highest_value = $end;
else
if ($highest_value < $end)
$highest_value = $end;
}
can be shortened to :
if ($lowest_value === null || $lowest_value > $start)
$lowest_value = $start;
if ($highest_value === null || $highest_value < $end)
$highest_value = $end;
Both values can be generated within one iteration without problems. Keep in mind that the < and > works in the case you keep the date formated as given. Otherwhise you would need to use date-functionality to compare the dates.
You could also use MySQL to query directly for the Min/Max values using the Aggregation methods Min or Max: http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html
(If you don't need each object to be loaded this will be faster)
I'm not sure if there's a way you couldn't do this in the select statement or not, I'll assume not. Get your result object into an array. This is super basic but should get the job done.
$lowest_value = null;
$highest_value = null;
foreach(result_object as $the_key => $the_value) {
if($the_key == 'start') {
if($lowest_value === null) {
$lowest_value = $the_value;
}
if(the_value < $lowest_value) {
$lowest_value = $the_value;
}
}
}
Do a second check for the end value just changing the operator

Accessing the properties of the last object in an array of objects?

Given that you have an array that contains varying amounts of objects, how can you access the properties of the last object? I tried to use end($array); but it gives the error: Object of class Post could not be converted to string
The class is:
class Post {
private $datafile;
public $index;
public $subIndex;
public $poster;
public $title;
public $message;
public $date;
// constructor and some unrelated methods here
public function getCommentData($givenIndex) {
$comments = null;
$data = file($this->datafile);
foreach($data as $row) {
list($index, $subIndex, $poster, $message, $date) = explode('|', $row);
$this->index = $subIndex; // SubIndex ties a Comment to a Post (same ID)
if($this->index == $givenIndex) {
$comment = new Post();
$comment->poster = $poster;
$comment->message = $message;
$comment->date = date(DATEFORMAT, strtotime($date));
$comments[] = $comment;
}
}
return $comments;
}
}
Now, I would like to access only the last Comment item's properties, but I'm not sure how it should be done? In a regular array, end() is quick and easy to use, but how about with objects as it doesn't seem to work?
Here is an example var_dump:
array (size=2)
0 =>
object(Post)[4]
private 'datafile' => null
public 'index' => null
public 'subIndex' => null
public 'poster' => string 'Postaaja' (length=8)
public 'title' => null
public 'message' => string 'Kommentti' (length=9)
public 'date' => string '5 Mar 2013 | 23:12' (length=18)
1 =>
object(Post)[5]
private 'datafile' => null
public 'index' => null
public 'subIndex' => null
public 'poster' => string 'Toinenkin' (length=9)
public 'title' => null
public 'message' => string 'Lisäkommentti' (length=14)
public 'date' => string '5 Mar 2013 | 23:13' (length=18)
Thanks!
EDIT:
Here's the way I tried to use it:
$comments = new Post(FILECOMMENTS);
$currentComments = $comments->getCommentData($i); // $i is the index of current newspost item
$newsComments = new Format();
$newsComments->formatShortComment($currentComments, $i);
And the method in the Format class:
// This comment is displayed as a short text in the main News view
public function formatShortComment($data, $index) {?>
<div class="newsComments">
<p class="newsPreviewComment">
<?php
$lastItem = end($data);
if(!empty($lastItem->message)) {
echo '<i>"',$lastItem->message,'"</i> ';
echo '-',$lastItem->poster;
}
?></p>
» Show All/Add comments
(<?php echo $commentCount; ?>)
</div><?php
}
You could try:
$tempArray = array_values($data);
$lastItem = $tempArray[count($tempArray)-1];
I hope I didn't miss something important, but if you're just trying to get the last element of a PHP array:
$lastItem = $data[count($data) - 1];

Categories