Removing duplicate objects from arrays? [duplicate] - php

Is there any method like the array_unique for objects? I have a bunch of arrays with 'Role' objects that I merge, and then I want to take out the duplicates :)

array_unique works with an array of objects using SORT_REGULAR:
class MyClass {
public $prop;
}
$foo = new MyClass();
$foo->prop = 'test1';
$bar = $foo;
$bam = new MyClass();
$bam->prop = 'test2';
$test = array($foo, $bar, $bam);
print_r(array_unique($test, SORT_REGULAR));
Will print:
Array (
[0] => MyClass Object
(
[prop] => test1
)
[2] => MyClass Object
(
[prop] => test2
)
)
See it in action here: http://3v4l.org/VvonH#v529
Warning: it will use the "==" comparison, not the strict comparison ("===").
So if you want to remove duplicates inside an array of objects, beware that it will compare each object properties, not compare object identity (instance).

Well, array_unique() compares the string value of the elements:
Note: Two elements are considered equal if and only if (string) $elem1 === (string) $elem2 i.e. when the string representation is the same, the first element will be used.
So make sure to implement the __toString() method in your class and that it outputs the same value for equal roles, e.g.
class Role {
private $name;
//.....
public function __toString() {
return $this->name;
}
}
This would consider two roles as equal if they have the same name.

This answer uses in_array() since the nature of comparing objects in PHP 5 allows us to do so. Making use of this object comparison behaviour requires that the array only contain objects, but that appears to be the case here.
$merged = array_merge($arr, $arr2);
$final = array();
foreach ($merged as $current) {
if ( ! in_array($current, $final)) {
$final[] = $current;
}
}
var_dump($final);

Here is a way to remove duplicated objects in an array:
<?php
// Here is the array that you want to clean of duplicate elements.
$array = getLotsOfObjects();
// Create a temporary array that will not contain any duplicate elements
$new = array();
// Loop through all elements. serialize() is a string that will contain all properties
// of the object and thus two objects with the same contents will have the same
// serialized string. When a new element is added to the $new array that has the same
// serialized value as the current one, then the old value will be overridden.
foreach($array as $value) {
$new[serialize($value)] = $value;
}
// Now $array contains all objects just once with their serialized version as string.
// We don't care about the serialized version and just extract the values.
$array = array_values($new);

You can also serialize first:
$unique = array_map( 'unserialize', array_unique( array_map( 'serialize', $array ) ) );
As of PHP 5.2.9 you can just use optional sort_flag SORT_REGULAR:
$unique = array_unique( $array, SORT_REGULAR );

You can also use they array_filter function, if you want to filter objects based on a specific attribute:
//filter duplicate objects
$collection = array_filter($collection, function($obj)
{
static $idList = array();
if(in_array($obj->getId(),$idList)) {
return false;
}
$idList []= $obj->getId();
return true;
});

From here: http://php.net/manual/en/function.array-unique.php#75307
This one would work with objects and arrays also.
<?php
function my_array_unique($array, $keep_key_assoc = false)
{
$duplicate_keys = array();
$tmp = array();
foreach ($array as $key=>$val)
{
// convert objects to arrays, in_array() does not support objects
if (is_object($val))
$val = (array)$val;
if (!in_array($val, $tmp))
$tmp[] = $val;
else
$duplicate_keys[] = $key;
}
foreach ($duplicate_keys as $key)
unset($array[$key]);
return $keep_key_assoc ? $array : array_values($array);
}
?>

If you have an indexed array of objects, and you want to remove duplicates by comparing a specific property in each object, a function like the remove_duplicate_models() one below can be used.
class Car {
private $model;
public function __construct( $model ) {
$this->model = $model;
}
public function get_model() {
return $this->model;
}
}
$cars = [
new Car('Mustang'),
new Car('F-150'),
new Car('Mustang'),
new Car('Taurus'),
];
function remove_duplicate_models( $cars ) {
$models = array_map( function( $car ) {
return $car->get_model();
}, $cars );
$unique_models = array_unique( $models );
return array_values( array_intersect_key( $cars, $unique_models ) );
}
print_r( remove_duplicate_models( $cars ) );
The result is:
Array
(
[0] => Car Object
(
[model:Car:private] => Mustang
)
[1] => Car Object
(
[model:Car:private] => F-150
)
[2] => Car Object
(
[model:Car:private] => Taurus
)
)

sane and fast way if you need to filter duplicated instances (i.e. "===" comparison) out of array and:
you are sure what array holds only objects
you dont need keys preserved
is:
//sample data
$o1 = new stdClass;
$o2 = new stdClass;
$arr = [$o1,$o1,$o2];
//algorithm
$unique = [];
foreach($arr as $o){
$unique[spl_object_hash($o)]=$o;
}
$unique = array_values($unique);//optional - use if you want integer keys on output

This is very simple solution:
$ids = array();
foreach ($relate->posts as $key => $value) {
if (!empty($ids[$value->ID])) { unset($relate->posts[$key]); }
else{ $ids[$value->ID] = 1; }
}

You can also make the array unique using a callback function (e.g. if you want to compare a property of the object or whatever method).
This is the generic function I use for this purpose:
/**
* Remove duplicate elements from an array by comparison callback.
*
* #param array $array : An array to eliminate duplicates by callback
* #param callable $callback : Callback accepting an array element returning the value to compare.
* #param bool $preserveKeys : Add true if the keys should be perserved (note that if duplicates eliminated the first key is used).
* #return array: An array unique by the given callback
*/
function unique(array $array, callable $callback, bool $preserveKeys = false): array
{
$unique = array_intersect_key($array, array_unique(array_map($callback, $array)));
return ($preserveKeys) ? $unique : array_values($unique);
}
Sample usage:
$myUniqueArray = unique($arrayToFilter,
static function (ExamQuestion $examQuestion) {
return $examQuestion->getId();
}
);

array_unique version for strict (===) comparison, preserving keys:
function array_unique_strict(array $array): array {
$result = [];
foreach ($array as $key => $item) {
if (!in_array($item, $result, true)) {
$result[$key] = $item;
}
}
return $result;
}
Usage:
class Foo {}
$foo1 = new Foo();
$foo2 = new Foo();
array_unique_strict( ['a' => $foo1, 'b' => $foo1, 'c' => $foo2] ); // ['a' => $foo1, 'c' => $foo2]

array_unique works by casting the elements to a string and doing a comparison. Unless your objects uniquely cast to strings, then they won't work with array_unique.
Instead, implement a stateful comparison function for your objects and use array_filter to throw out things the function has already seen.

This is my way of comparing objects with simple properties, and at the same time receiving a unique collection:
class Role {
private $name;
public function __construct($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
}
$roles = [
new Role('foo'),
new Role('bar'),
new Role('foo'),
new Role('bar'),
new Role('foo'),
new Role('bar'),
];
$roles = array_map(function (Role $role) {
return ['key' => $role->getName(), 'val' => $role];
}, $roles);
$roles = array_column($roles, 'val', 'key');
var_dump($roles);
Will output:
array (size=2)
'foo' =>
object(Role)[1165]
private 'name' => string 'foo' (length=3)
'bar' =>
object(Role)[1166]
private 'name' => string 'bar' (length=3)

If you have array of objects and you want to filter this collection to remove all duplicates you can use array_filter with anonymous function:
$myArrayOfObjects = $myCustomService->getArrayOfObjects();
// This is temporary array
$tmp = [];
$arrayWithoutDuplicates = array_filter($myArrayOfObjects, function ($object) use (&$tmp) {
if (!in_array($object->getUniqueValue(), $tmp)) {
$tmp[] = $object->getUniqueValue();
return true;
}
return false;
});
Important: Remember that you must pass $tmp array as reference to you filter callback function otherwise it will not work

Related

How to add and index dimensions of arrays in php dynamically?

I have an array in a PHP class and two member functions
First one receives two integers, one is the dimension and the other is the value:
private $complexArray;
function setValueToGivenDimension($dimension, $value)
What I want to do is to set the value to the given dimension of the array.
For example, If I call the function as the following:
setValueToGivenDimension(3,"key","myValue")
I want the array to be
array [
0 => array [
0 => array [
"key" => "myValue"
]
]
]
And Second is function getValueOfGivenDimension($dimension, $key)
For example, If I call the function as the following:
getValueOfGivenDimension(3,"key")
It should return value of the given key which is 0 in this case of the 3rd dimension of the $complexArray:
"myValue"
*array can have any level of dimension and I want to create and index dimensions of the array dynamically.
Here is a way to do it. Note the use of & to get a reference on the array:
function setValueToGivenDimension(&$array, $dimension, $key, $value)
{
for($i=1;$i<$dimension;$i++)
{
if(!isset($array[0]))
$array[0] = [];
$array = &$array[0];
}
$array[$key] = $value;
}
function getValueOfGivenDimension($array, $dimension, $key)
{
for($i=1;$i<$dimension;$i++)
{
if(!isset($array[0]))
return null;
$array = $array[0];
}
if(!isset($array[$key]))
return null;
return $array[$key];
}
$complexArray = [];
setValueToGivenDimension($complexArray, 3, 'key', 'value');
echo getValueOfGivenDimension($complexArray, 3, 'key');
You can use recursion for both.
The following method will return the array you desire, so you can set your private variable to the output.
function setValueToGivenDimension($dimension, $value) {
// Base case: if the dimension is 0, then we should return the value
if ($dimension == 0) return $value;
// If the dimension is greater than 0, then return the recursive function call, wrapped in a new array
return [setValueToGivenDimension($dimension - 1, $value)];
}
The following method will take an array, dimension, and key, and output the value for the nth dimension of that array, using the key in the innermost dimension.
function getValueOfGivenDimension($array, $dimension, $key) {
// Base case: if the function is at dimension 1, then return the value at the given key for the array
if ($dimension == 1) return $array[$key];
// If the dimension is greater than 0, then recursively call the function on the only child of the given array
return getValueOfGivenDimension($array[0], $dimension - 1, $key);
}
Based on Olivers answer, here is the code within a class:
<?php
class MyClass
{
private $complexArray;
/**
* #return void
*/
public function doSomething()
{
$this->setValueToGivenDimension(3, 'key', 'value');
print_r($this->complexArray);
echo $this->getValueOfGivenDimension(3, 'key') . PHP_EOL;
}
/**
* #param int $dimension
* #param mixed $key
* #param mixed $value
* #return $this
*/
private function setValueToGivenDimension($dimension, $key, $value)
{
if (!is_array($this->complexArray)) {
$this->complexArray = [];
}
if ($dimension > 0) {
$array = &$this->complexArray;
for ($i = 1; $i < $dimension; $i++) {
if (!isset($array[0])) {
$array[0] = [];
}
$array = &$array[0];
}
$array[$key] = $value;
}
return $this;
}
/**
* #param int $dimension
* #param mixed $key
* #return mixed|null
*/
private function getValueOfGivenDimension($dimension, $key)
{
if ($dimension > 0) {
$array = $this->complexArray;
for ($i = 1; $i < $dimension; $i++) {
if (!isset($array[0])) {
return null;
}
$array = $array[0];
}
if (!isset($array[$key])) {
return null;
}
return $array[$key];
}
return null;
}
}
Run it:
$mc = new MyClass();
$mc->doSomething();
Output:
Array
(
[0] => Array
(
[0] => Array
(
[key] => value
)
)
)
value

Elegant way to search an PHP array using a user-defined function

Basically, I want to be able to get the functionality of C++'s find_if(), Smalltalk's detect: etc.:
// would return the element or null
check_in_array($myArray, function($element) { return $elemnt->foo() > 10; });
But I don't know of any PHP function which does this. One "approximation" I came up with:
$check = array_filter($myArray, function($element) { ... });
if ($check)
//...
The downside of this is that the code's purpose is not immediately clear. Also, it won't stop iterating over the array even if the element was found, although this is more of a nitpick (if the data set is large enough to cause problems, linear search won't be an answer anyway)
To pull the first one from the array, or return false:
current(array_filter($myArray, function($element) { ... }))
More info on current() here.
Here's a basic solution
function array_find($xs, $f) {
foreach ($xs as $x) {
if (call_user_func($f, $x) === true)
return $x;
}
return null;
}
array_find([1,2,3,4,5,6], function($x) { return $x > 4; }); // 5
array_find([1,2,3,4,5,6], function($x) { return $x > 10; }); // null
In the event $f($x) returns true, the loop short circuits and $x is immediately returned. Compared to array_filter, this is better for our use case because array_find does not have to continue iterating after the first positive match has been found.
In the event the callback never returns true, a value of null is returned.
Note, I used call_user_func($f, $x) instead of just calling $f($x). This is appropriate here because it allows you to use any compatible callable
Class Foo {
static private $data = 'z';
static public function match($x) {
return $x === self::$data;
}
}
array_find(['x', 'y', 'z', 1, 2, 3], ['Foo', 'match']); // 'z'
Of course it works for more complex data structures too
$data = [
(object) ['id' => 1, 'value' => 'x'],
(object) ['id' => 2, 'value' => 'y'],
(object) ['id' => 3, 'value' => 'z']
];
array_find($data, function($x) { return $x->id === 3; });
// stdClass Object (
// [id] => 3
// [value] => z
// )
If you're using PHP 7, add some type hints
function array_find(array $xs, callable $f) { ...
The original array_search returns the key of the matched value, and not the value itself (this might be useful if you're will to change the original array later).
try this function (it also works will associatives arrays)
function array_search_func(array $arr, $func)
{
foreach ($arr as $key => $v)
if ($func($v))
return $key;
return false;
}
Use \iter\search() from nikic's iter library of primitive iteration functions. It has the added benefit that it operates on both arrays and Traversable collections.
$foundItem = \iter\search(function ($item) {
return $item > 10;
}, range(1, 20));
if ($foundItem !== null) {
echo $foundItem; // 11
}
Pulled from Laravel's Illuminate\Collections\Arr::first method:
if (!function_exists('array_first')) {
/**
* Return the first element in an array passing a given truth test.
*
* #param iterable $array
* #param callable|null $callback
* #param mixed $default
* #return mixed
*/
function array_first($array, callable $callback = null, $default = null)
{
if (is_null($callback)) {
if (empty($array)) {
return $default;
}
foreach ($array as $item) {
return $item;
}
}
foreach ($array as $key => $value) {
if ($callback($value, $key)) {
return $value;
}
}
return $default;
}
}
I think it's pretty good. There is also the Illuminate\Collections\Arr::last method, but it's probably not as optimized since it reverses the array and just calls the first method. It does get the job done, though.
if (!function_exists('array_last')) {
/**
* Return the last element in an array passing a given truth test.
*
* #param array $array
* #param callable|null $callback
* #param mixed $default
* #return mixed
*/
function array_last($array, callable $callback = null, $default = null)
{
if (is_null($callback)) {
return empty($array) ? $default : end($array);
}
return array_first(array_reverse($array, true), $callback, $default);
}
}
Pro-tip: If you have an array of objects, then you can specify the type of the callback's argument for that sweet IDE auto-completion.
$john = array_first($users, function(User $user) {
return $user->name === 'John';
});
// Works with pretty much anything.
$activeUsers = array_filter($users, function(User $user) {
return $user->isActive;
});
// Class example:
class User {
public string $name;
public bool $isActive;
//...
}
If you want to use some variable from the outer scope, you can use the use(&$var) syntax like this
foreach($values as $key => $value) {
array_find($conditionsRecords, function($row) use(&$key) {
$keyToFind = $key;
return $keyToFind;
})
}
You can write such a function yourself, although it is little more than a loop.
For instance, this function allows you to pass a callback function. The callback can either return 0 or a value. The callback I specify returns the integer if it is > 10. The function stops when the callback returns a non null value.
function check_in_array(array $array, $callback)
{
foreach($array as $item)
{
$value = call_user_func($callback, $item);
if ($value !== null)
return $value;
}
}
$a = array(1, 2, 3, 6, 9, 11, 15);
echo check_in_array($a, function($i){ return ($i > 10?$i:null); });
You can write your own function ;)
function callback_search ($array, $callback) { // name may vary
return array_filter($array, $callback);
}
This maybe seems useless, but it increases semantics and can increase readability

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]);

PHP array with distinct elements (like Python set)

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;
}

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