PHP array with distinct elements (like Python set) - php

Is there a version of the PHP array class where all the elements must be distinct like, for instance, sets in Python?

Nope. You could fake it by using an associative array where the keys are the elements in the "set" and the values are ignored.

Here's a first-draft of an idea that could eventually work for what you want.
<?php
class DistinctArray implements IteratorAggregate, Countable, ArrayAccess
{
protected $store = array();
public function __construct(array $initialValues)
{
foreach ($initialValues as $key => $value) {
$this[$key] = $value;
}
}
final public function offsetSet( $offset, $value )
{
if (in_array($value, $this->store, true)) {
throw new DomainException('Values must be unique!');
}
if (null === $offset) {
array_push($this->store, $value);
} else {
$this->store[$offset] = $value;
}
}
final public function offsetGet($offset)
{
return $this->store[$offset];
}
final public function offsetExists($offset)
{
return array_key_exists($offset, $this->store);
}
final public function offsetUnset($offset)
{
unset( $this->store[$offset] );
}
final public function count()
{
return count($this->store);
}
final public function getIterator()
{
return new ArrayIterator($this->store);
}
}
$test = new DistinctArray(array(
'test' => 1,
'foo' => 2,
'bar' => 3,
'baz' => '1',
8 => 4,
));
try {
$test[] = 5;
$test[] = 6;
$test['dupe'] = 1;
}
catch (DomainException $e) {
echo "Oops! ", $e->getMessage(), "<hr>";
}
foreach ($test as $value) {
echo $value, '<br>';
}

You can use a special class, or array_unique to filter the duplicates.

An array is an array, and for the most part yo can put anything into it. All keys must be unique. If you want to go and add a function that strips out duplicate values, that is very possible by simply doing a array_unique statement

For objects that are not integers and strings: SplObjectStorage
The SplObjectStorage class provides a map from objects to data or, by ignoring data, an object set.

You can use this Set class. You can install with pecl
sudo pecl install ds
there is also a polyfill version available if u have no root access
composer require php-ds/php-ds

In a basic way you could try array_unique(), maybe this could help to avoid duplicates in your array.

You cannot use array_unique.
If you use int and string values, array_unique uses a string-representation for comparison, so array [1,'1','2'] will result in [1,'2'].

You can trick
Array Key index are unique.
So you can store unique val as string keys, like a python set.
$liste , is just a bad exemple for simulate datas.
$liste = ['1','1','2','3','4'];
$uniq = [];
for ($liste as elem) {
$uniq[$elem] = 1;
}
$uniq = array_keys($uniq);
// Or use directly
for ($uniq as $uniqVal => $null) {
echo $uniqVal;
}

Related

Dynamic assignment of property names based on array values

I am trying to create a class that is going to generate dynamic class properties according to a user input.
There will be an array created from user input data. This array should work as an example:
$array = array(
# The boolean values are not relevant in this example
# The keys are important
'apple' => true,
'orange' => false,
'pear' => false,
'banana' => true,
);
Right now I want to create a new class with the array keys as the class properties:
class Fruit {
public $apple;
public $orange;
public $pear;
public $banana;
(etc.)
}
I had to manually write down all four properties now.
Is there a way to make it automated?
<?php
class MyClass
{
public function __construct ($config = [])
{
foreach ($config as $key => $value) {
$this->{$key} = $value;
}
}
}
$myClass = new MyClass(['apple' => 1, 'orange' => 2]);
echo $myClass->apple;
?>
this should help you
Here you go,
I put a few bonus things in there:
class MyClass implements Countable, IteratorAggregate
{
protected $data = [];
public function __construct (array $data = [])
{
foreach ($data as $key => $value) {
$this->{$key} = $value;
}
}
public function __set($key, $value){
$this->data[$key] = $value;
}
public function __get($key)
{
if(!isset($this->{$key})) return null; //you could also throw an exception here.
return $this->data[$key];
}
public function __isset($key){
return isset($this->data[$key]);
}
public function __unset($key){
unset($this->data[$key]);
}
public function __call($method, $args){
$mode = substr($method, 0, 3);
$property = strtolower(substr($method, 3)); //only lowercase properties
if(isset($this->{$property})) {
if($mode == 'set'){
$this->{$property} = $args[0];
return null;
}else if($mode == 'get'){
return $this->{$property};
}
}else{
return null; //or throw an exception/remove this return
}
throw new Exception('Call to undefined method '.__CLASS__.'::'.$method);
}
//implement Countable
public function count(){
return count($this->data);
}
//implementIteratorAggregate
public function getIterator() {
return new ArrayIterator($this->data);
}
}
Test it:
$myClass = new MyClass(['one' => 1, 'two' => 2]);
echo $myClass->two."\n";
//Countable
echo count($myClass)."\n";
//dynamic set
$myClass->three = 3;
echo count($myClass)."\n";
//dynamic get/set methods. I like camel case methods, and lowercase properties. If you don't like that then you can change it.
$myClass->setThree(4);
echo $myClass->getThree()."\n";
//IteratorAggregate
foreach($myClass as $key=>$value){
echo $key.' => '.$value."\n";
}
Outputs
2 //value of 2
2 //count of $data
3 //count of $data after adding item
4 //value of 3 after changing it with setThree
//foreach output
one => 1
two => 2
three => 4
Test it online
Disclamer
Generally though it's better to define the class by hand, that way things like IDE's work. You may also have issues because you won't necessarily know what is defined in the class ahead of time. You don't have a concrete definition of the class as it were.
Pretty much any method(at least in my code) that starts with __ is a PHP magic method (yes, that's a thing). When I first learned how to use these I thought it was pretty cool, but now I almost never use them...
Now if you want to create an actual .php file with that code in it, that's a different conversation. (it wasn't 100% clear, if you wanted functionality or an actual file)
Cheers.

Use list() with an object in PHP

I would like to use the list() statement in combination with an object.
$tuple = new Tuple();
// ..
list ($guestbook, $entry, $author) = $tuple;
This would work if $tuple was an array with three elements. But its an object.
Is there any way without using a method of Tuple (returning that kind of array) like implementing a fancy native interface I yet don't know?
You can implement the interface ArrayAccess to do so:
class Tuple implements ArrayAccess {
private $arr;
public function __construct($arr) {
$this->arr = $arr;
}
public function offsetExists($offset) {
return array_key_exists($offset, $this->arr);
}
public function offsetGet($offset) {
return $this->arr[$offset];
}
public function offsetSet($offset, $value) {
return $this->arr[$offset] = $value;
}
public function offsetUnset($offset) {
unset($this->arr[$offset]);
}
}
$tuple = new Tuple([1, 2, 3]);
list($am, $stram, $gram) = $tuple;
echo $am;
echo $stram;
echo $gram;
// outputs: 123
See this previous post:
Convert PHP object to associative array
I am assuming (I haven't tested it) you could then do:
$tuple = new Tuple();
list ($guestbook, $entry, $author) = (array) $tuple;
You can do this:
$tumple = new Tumple();
$properties = get_object_vars($tumple);// ["guestbook" => "Feel", "entry" => "Good", "author" => "Inc"];

in_array - 'in_object' equivalent?

Is there such a function like in_array, but can be used on objects?
Nope, but you can cast the object to an array and pass it into in_array().
$obj = new stdClass;
$obj->one = 1;
var_dump(in_array(1, (array) $obj)); // bool(true)
That violates all kinds of OOP principles though. See my comment on your question and Aron's answer.
First of all, arrays and objects are quite different.
A PHP object can not be iterated through like an array, by default. A way to implement object iteration is to implement the Iterator interface.
Concerning your specific question, you probably want to take a look at the ArrayAccess interface:
class obj implements ArrayAccess {
private $container = array();
public function __construct() {
$this->container = array(
"one" => 1,
"two" => 2,
"three" => 3,
);
}
public function offsetSet($offset, $value) {
if (is_null($offset)) {
$this->container[] = $value;
} else {
$this->container[$offset] = $value;
}
}
public function offsetExists($offset) {
return isset($this->container[$offset]);
}
public function offsetUnset($offset) {
unset($this->container[$offset]);
}
public function offsetGet($offset) {
return isset($this->container[$offset]) ? $this->container[$offset] : null;
}
}
Now you can access your object like an array in the following manner:
$object = new obj();
var_dump(isset($obj['two'])); // exists!
var_dump(isset($obj['foo'])); // does not exist
Before you go crazy on this though, please consider why you are actually trying to do this and take a look at the examples at php.net.
Option 2: when you are simply trying to see if a property exists, you can use property_exists() for this:
class foo {
public $bar = 'baz';
}
$object = new foo();
var_dump(property_exists($object, 'bar')); // true
You could cast the object to an array:
$obj = new stdClass();
$obj->var = 'foobar';
in_array( 'foobar', (array)$obj ); // true
function in_object($needle, $haystack) {
return in_array($needle, get_object_vars($haystack));
}
It's unbelievable how all the people miss the point of the usefulness of an in_object PHP method! Here is what I came up with, it is very useful, and you will see why!
Here is a simple function I wrote which will check if a value can be found within an object.
<?php
// in_object method
// to check if a value in an object exists.
function in_object($value,$object) {
if (is_object($object)) {
foreach($object as $key => $item) {
if ($value==$item) return $key;
}
}
return false;
}
?>
This is very useful if an object has been created dynamically (especially from external code, which you don't control, as in an application-plugin, CMS, etc), and you don't know the object's properties.
The above function will return the property, so you will be able to use it in your code later on.
Here is a very good basic example of how useful this function is!
<?php
class My_Class {
function __construct($key, $value) {
$this->$key = $value;
// As you can see, this is a dynamic class, its properties and values can be unknown...
}
}
function in_object($value,$object) {
if (is_object($object)) {
foreach($object as $key => $item) {
if ($value==$item) return $key;
}
}
return false;
}
function manipulate_property($value,$object) {
if ($property = in_object($value,$object)) {
// value found. I can now use this property.
// I can simply echo'it (makes no sense, as I could instead simply echo "value")
echo "<br />I found the property holding this value: ".$object->$property;
// or (here comes the good part)
// change the property
$object->$property = "This is a changed value!";
echo "<br />I changed the value to: ".$object->$property;
// or return it for use in my program flow
return $property;
} else {
echo "Value NOT FOUND!<br />";
return false;
}
}
// imagine if some function creates the class conditionally...
if ( 1 == 1) {
$class = new My_Class("property","Unchanged Value");
} else {
$class = new My_Class("property","Some Other Value");
}
// now let's check if the value we want exists, and if yes, let's have some fun with it...
$property = manipulate_property("Unchanged Value",$class);
if ($property) {
$my_variable = $class->$property;
echo "<br />This is my variable now:".$my_variable;
} else $my_variable = $some_other_variable;
?>
Just run it to see for yourself!
I don't recommend it, because it's very bad practice but you can use get_object_vars.
Gets the accessible non-static properties of the given object according to scope.
There are other limitations you should refer to the documentation to see if it is suitable for you.
if(in_array('find me', get_object_vars($obj)))
This is the most efficient and correct solution. With some modifications it could be applied to check any data type present in any object.
if(gettype($object->var1->var2) == "string"){
echo "Present";
}

PHP : Remove object from array

What is an elegant way to remove an object from an array of objects in PHP?
class Data{
private $arrObservers;
public add(Observer $o) {
array_push($this->arrObservers, $o);
}
public remove(Observer $o) {
// I NEED THIS CODE to remove $o from $this->arrObservers
}
}
You can do
function unsetValue(array $array, $value, $strict = TRUE)
{
if(($key = array_search($value, $array, $strict)) !== FALSE) {
unset($array[$key]);
}
return $array;
}
You can also use spl_object_hash to create a hash for the objects and use that as array key.
However, PHP also has a native Data Structure for Object collections with SplObjectStorage:
$a = new StdClass; $a->id = 1;
$b = new StdClass; $b->id = 2;
$c = new StdClass; $c->id = 3;
$storage = new SplObjectStorage;
$storage->attach($a);
$storage->attach($b);
$storage->attach($c);
echo $storage->count(); // 3
// trying to attach same object again
$storage->attach($c);
echo $storage->count(); // still 3
var_dump( $storage->contains($b) ); // TRUE
$storage->detach($b);
var_dump( $storage->contains($b) ); // FALSE
SplObjectStorage is Traversable, so you can foreach over it as well.
On a sidenote, PHP also has native interfaces for Subject and Observer.
I agree with the answers above, but for the sake of completeness (where you may not have unique IDs to use as a key) my preferred methods of removing values from an array are as follows:
/**
* Remove each instance of a value within an array
* #param array $array
* #param mixed $value
* #return array
*/
function array_remove(&$array, $value)
{
return array_filter($array, function($a) use($value) {
return $a !== $value;
});
}
/**
* Remove each instance of an object within an array (matched on a given property, $prop)
* #param array $array
* #param mixed $value
* #param string $prop
* #return array
*/
function array_remove_object(&$array, $value, $prop)
{
return array_filter($array, function($a) use($value, $prop) {
return $a->$prop !== $value;
});
}
Which are used in the following way:
$values = array(
1, 2, 5, 3, 5, 6, 7, 1, 2, 4, 5, 6, 6, 8, 8,
);
print_r(array_remove($values, 6));
class Obj {
public $id;
public function __construct($id) {
$this->id = $id;
}
}
$objects = array(
new Obj(1), new Obj(2), new Obj(4), new Obj(3), new Obj(6), new Obj(4), new Obj(3), new Obj(1), new Obj(5),
);
print_r(array_remove_object($objects, 1, 'id'));
I recommend using the ID (if you have one, anything that will be unique to that object should work within reason) of the object as the array key. This way you can address the object within the array without having to run through a loop or store the ID in another location. The code would look something like this:
$obj_array[$obj1->getId()] = $obj1;
$obj_array[$obj2->getId()] = $obj2;
$obj_array[$obj3->getId()] = $obj3;
unset($obj_array[$object_id]);
UPDATE:
class Data{
private $arrObservers;
public add(Observer $o) {
$this->arrObservers[$o->getId()] = $o;
}
public remove(Observer $o) {
unset($this->arrObservers[$o->getId()]);
}
}
unset($myArray[$index]); where $index is the index of the element you want to remove. If you wan't a more specific answer, show some code or describe what you're trying to do.
$obj_array['obj1'] = $obj1;
$obj_array['obj2'] = $obj2;
$obj_array['obj3'] = $obj3;
unset($obj_array['obj3']);
For remove an object from a multi dimensional array you can use this:
$exampleArray= [
[
"myKey"=>"This is my key",
"myValue"=>"10"
],
[
"myKey"=>"Oh!",
"myValue"=>"11"
]
];
With array_column you can specify your key column name:
if(($key = array_search("Oh!", array_column($exampleArray, 'myKey'))) !== false) {
unset($exampleArray[$key]);
}
And this will remove the indicated object.
Use this for your internal object storage instead: http://us2.php.net/manual/en/class.splobjectstorage.php
function obj_array_clean ($array, $objId)
{
$new = array() ;
foreach($array as $value)
{
$new[$value->{$objId}] = $value;
}
$array = array_values($new);
return $array;
}
$ext2 = obj_array_clean($ext, 'OnjId');
It will remove the duplicate object "OnjId" from array objects $array.
If you want to remove one or more objects from array of objects (using spl_object_hash to determine if objects are the same) you can use this method:
$this->arrObservers = Arr::diffObjects($this->arrObservers, [$o]);
from this library.
Reading the Observer pattern part of the GoF book? Here's a solution that will eliminate the need to do expensive searching to find the index of the object that you want to remove.
public function addObserver(string $aspect, string $viewIndex, Observer $view)
{
$this->observers[$aspect][$viewIndex] = $view;
}
public function removeObserver(string $aspect, string $viewIndex)
{
if (!isset($this->observers[$aspect])) {
throw new OutOfBoundsException("No such aspect ({$aspect}) of this Model exists: " . __CLASS__);
}
if (!isset($this->observers[$aspect][$viewIndex])) {
throw new OutOfBoundsException("No such View for ({$viewIndex}) was added to the aspect ({$aspect}) of this Model:" . __CLASS__);
}
unset($this->observers[$aspect][$viewIndex]);
}
You can loose the "aspect" dimension if you are not using that way of keeping track of which Views are updated by specific Models.
public function addObserver(string $viewIndex, Observer $view)
{
$this->observers[$viewIndex] = $view;
}
public function removeObserver(string $viewIndex)
{
if (!isset($this->observers[$viewIndex])) {
throw new OutOfBoundsException("No such View for ({$viewIndex}) was added to this Model:" . __CLASS__);
}
unset($this->observers[$viewIndex]);
}
Summary
Build in a way to find the element before assigning the object to the array. Otherwise, you will have to discover the index of the object element first.
If you have a large number of object elements (or, even more than a handful), then you may need to resort to finding the index of the object first. The PHP function array_search() is one way to start with a value, and get the index/key in return.
https://www.php.net/manual/en/function.array-search.php
Do be sure to use the strict argument when you call the function.
If the third parameter strict is set to true then the array_search()
function will search for identical elements in the haystack. This
means it will also perform a strict type comparison of the needle in
the haystack, and objects must be the same instance.
Try this, will solve your problem.
class Data{
private $arrObservers;
public add(Observer $o) {
array_push($this->arrObservers,$o);
}
public remove($Observer $o) {
unset($this->arrObservers[$o]);
}
}
I believe this is the best way
$index = array_search($o, $this->arrObservers, true);
unset($this->arrObservers[$index]);

Array with associative values accessed numerically

I have an associative array that I might need to access numerically (ie get the 5th key's value).
$data = array(
'one' => 'something',
'two' => 'else',
'three' => 'completely'
) ;
I need to be able to do:
$data['one']
and
$data[0]
to get the same value, 'something'.
My initial thought is to create a class wrapper that implements ArrayAccess with offsetGet() having code to see if the key is numeric and act accordingly, using array_values:
class MixedArray implements ArrayAccess {
protected $_array = array();
public function __construct($data) {
$this->_array = $data;
}
public function offsetExists($offset) {
return isset($this->_array[$offset]);
}
public function offsetGet($offset) {
if (is_numeric($offset) || !isset($this->_array[$offset])) {
$values = array_values($this->_array) ;
if (!isset($values[$offset])) {
return false ;
}
return $values[$offset] ;
}
return $this->_array[$offset];
}
public function offsetSet($offset, $value) {
return $this->_array[$offset] = $value;
}
public function offsetUnset($offset) {
unset($this->_array[$offset]);
}
}
I am wondering if there isn't any built in way in PHP to do this. I'd much rather use native functions, but so far I haven't seen anything that does this.
Any ideas?
Thanks,
Fanis
how about this
$data = array(
'one' => 'something',
'two' => 'else',
'three' => 'completely'
) ;
then
$keys = array_keys($data);
Then
$key = $keys[$index_you_want];
Then
echo $data[$key];
There isn't a built-in way to do this.
If it's a one-off thing you could use something like:
$array = array_merge($array, array_values($array));
This won't update as you add new items to the array though.
i noticed you mentioned it is a readonly database result set
if you are using mysql then you could do something like this
$result = mysql_query($sql);
$data = mysql_fetch_array($result);
mysql_fetch_array returns an array with both associative and numeric keys
http://nz.php.net/manual/en/function.mysql-fetch-array.php
Sometimes it's easier to check if you have an associative key or an index with is_int()
if(is_int($key))
return current(array_slice($data, $key, 1));
else
return $data[$key];

Categories