Remove duplicates from array but ignoring one parameter - php

I have an array comprised of PHP objects as such:
$objects[0] => $object->type => 'President'
$object->name => 'Joe Blogs'
$object->address => '123 Harry Street'
$objects[1] => $object->type => 'Secretary'
$object->name => 'Joe Blogs'
$object->address => '123 Harry Street'
$objects[2] => $object->type => 'Treasurer'
$object->name => 'Jane Doe'
$object->address => '456 Upton Street'
I would like to ignore the type parameter and end up with:
$objects[0] => $object->type => 'President'
$object->name => 'Joe Blogs'
$object->address => '123 Harry Street'
$objects[2] => $object->type => 'Treasurer'
$object->name => 'Jane Doe'
$object->address => '456 Upton Street'
I have tried a few different things, one of which was to unset the parameter type using a foreach loop and then trying to reset it, but I wasn't sure how to tie the two indexes together to reset them. Another was trying to use the union in the select command but that wasn't working 100% correctly either.
I am just not sure how to best manage the type parameter.
EDIT:
I have tried to make the query a little easier, it now returns a list of IDs that I will get the address information later. This is the new array that I would like to filter out any duplicates.
$items['president'][0] = '1'
[1] = '2'
[2] = '3'
$items['secretary'][0] = '1'
[1] = '4'
[2] = '5'
What I would like is
$items['president'][0] = '1'
[1] = '2'
[2] = '3'
$items['secretary'][1] = '4'
[2] = '5'
Is that any better? (Note: I can use both array structures, but the second one would be better)

This should work for you:
Here I just go through each object from $objects. Then I go through all of your unique objects from $unique with array_reduce() and check if there is one object, which has the same values in name and address as the current one of the iteration.
If there in no object with the same values name and address in $unique I add it to it.
<?php
$unique = [];
foreach($objects as $o) {
$duplicate = array_reduce($unique, function($keep, $v)use($o){
if($v->name == $o->name && $v->address == $o->address)
return $keep = TRUE;
}, FALSE);
if(!$duplicate)
$unique[] = $o;
}
print_r($unique);
?>
output:
Array
(
[0] => stdClass Object
(
[type] => President
[name] => Joe Blogs
[address] => 123 Harry Street
)
[1] => stdClass Object
(
[type] => Treasurer
[name] => Jane Doe
[address] => 456 Upton Street
)
)
EDIT:
As from your updated question with the new array structure, something like this should work:
Just like before you have an array with the unique values and also an array to keep track on which values you already have in your $unique array. Then you can just loop through your array an check if the value isn't already in the values array.
<?php
$unique = [];
$values = [];
foreach($items as $keyOne => $arr) {
foreach($arr as $keyTwo =>$v) {
if(!in_array($v, $values)) {
$unique[$keyOne][$keyTwo] = $v;
$values[] = $v;
}
}
}
print_r($unique);
?>
output:
Array
(
[president] => Array
(
[0] => 1
[1] => 2
[2] => 3
)
[secretary] => Array
(
[1] => 4
[2] => 5
)
)

you can just use array_unique, this function compares objects using the toString method:
class obj{
public $type;
public $name;
public $address;
public function __construct($type, $name, $address){
$this->type = $type;
$this->name = $name;
$this->$address = $address;
}
public function __toString(){
return $this->name . $this->address;
}
}
$objects = array(
new obj('President','Joe Bogs','123 Harry Street'),
new obj('Treasurer','Joe Bogs','123 Harry Street'),
new obj('Secretary','Jane Doh','456 Harry Street'),
);
// array_unique compares the toString value if given objects
$unique = array_unique($objects);
Or, if for some Reason you can not use __toString in your Object, write your own unique function that can compare using a custom function that returns a unique_id as you want it:
function custom_array_unique($array, $function){
$res = array();
foreach($array as $o){
if(!isset($res[$function($o)])) {
$res[$function($o)] = $o;
}
}
return array_values($res);
}
$unique2 = custom_array_unique($objects, function($obj){
return $obj->name . $obj->address;
});
var_dump($unique2);

You can take the type as key, so it won't be duplicated, and in case you don't want to override, you can just verify it's existence by using array_key_exists, and after that you can array_values to get the order as required, see example below:
$newArray = array();
foreach($objects as $object){
if(!array_key_exists($object->name, $newArray)){
$newArray[$object->name] = $object;
}
}
$newArray = array_values($newArray);
echo '<pre>';
print_r($newArray);

Related

PHP - Count unique in an array of objects

I have a PHP array which contains objects like this
Array
(
[0] => stdClass Object
(
[label] => Test 1
[session] => 2
)
[1] => stdClass Object
(
[label] => Test 2
[session] => 2
)
[2] => stdClass Object
(
[label] => Test 3
[session] => 42
)
[3] => stdClass Object
(
[label] => Test 4
[session] => 9
)
)
I am trying to count the number of unique sessions within this array. I can do it when the whole thing is an array, but I am struggling to work it out when the array contains objects.
Do I need to convert the objects into arrays or is there a way of doing it with the data in its current format?
https://www.php.net/manual/en/function.array-column.php :
Version Description
7.0.0 Added the ability for the input parameter to be an array of objects.
Use array_column to generate new keys using the session column values. This effectively removes duplicate keys.
Code: (Demo)
$array = [
(object)['label' => 'Test 1', 'session' => 2],
(object)['label' => 'Test 2', 'session' => 2],
(object)['label' => 'Test 3', 'session' => 42],
(object)['label' => 'Test 4', 'session' => 9],
];
echo sizeof(array_column($array, null, 'session'));
Output:
3
Or in a loop:
foreach ($array as $obj) {
$result[$obj->session] = null;
}
echo sizeof($result);
Both techniques avoid the extra function call of array_unique and leverage the fact that arrays cannot store duplicate keys.
I have tried your code and here created sample data
$comments= array();
$comment = new stdClass;
$comment->label = 'Test 1';
$comment->session = '2';
array_push($comments, $comment);
$comment = new stdClass;
$comment->label = 'Test 2';
$comment->session = '2';
array_push($comments, $comment);
$comment = new stdClass;
$comment->label = 'Test 3';
$comment->session = '42';
array_push($comments, $comment);
$comment = new stdClass;
$comment->label = 'Test 4';
$comment->session = '9';
array_push($comments, $comment);
Here is code I tried to get the unique value. this way you can get any field unique value
$uniques = array();
foreach ($comments as $obj) {
$uniques[$obj->session] = $obj;
}
echo "<pre>";
print_r($uniques);
echo "</pre>";
You could also use array_map to only keep the value of session in the array, then use array_unique to remove the duplicate entries and finally count the unique items.
If for example your array variable is called $array:
$result = array_map(function($x){
return $x->session;
}, $array);
echo count(array_unique($result));
That will result in:
3
Demo
The object within an array can be accessed with $array[0] where the 0 stands for the object. To access the objects property you can do $object->session.
To go throught every objects session property you can do:
foreach ($array as $object) {
echo $object->session . "<br/>";
}

PHP combine several arrays to one

I want to combine several arrays into one, they are the result of a form post with an unknown number of elements, eg:
$ids = [53,54,55];
$names = ['fire','water','earth'];
$temps = [500,10,5];
What i want is to make a function that takes these arrays as an input and produces a single output, like
$elements = [['id'=>53,'name'=>'fire','temp'=>500] , ['id'=>54,'name'=>'water','temp'=>500] , ['id'=>55,'name'=>'earth','temp'=>500]]
I came up with the following solution:
function atg($array) {
$result = array();
for ($i=0;$i<count(reset($array));$i++) {
$newAr = array();
foreach($array as $index => $val) {
$newAr[$index] = $array[$index][$i];
}
$result[]=$newAr;
}
return $result;
}
It can be called like
$elements = atg(['id' => $ids, 'name' => $names, 'temp' => $temps]);
And it produces the right output. To me it seems a bit overly complicated though, and I'm sure this is a common problem in PHP for form posts, combining seperate fields into a single array per item. What would be a better solution?
You can loop through all of your 3 arrays at once with array_map(). There you can just return the new array with a value of each of the 3 arrays, e.g.
$result = array_map(function($id, $name, $temp){
return ["id" => $id, "name" => $name, "temp" => $temp];
}, $ids, $names, $temps);
Use below code:-
$ids = [53,54,55];
$names = ['fire','water','earth'];
$temps = [500,10,5];
$result = [];
foreach($ids as $k=>$id){
$result[$k]['id'] = $id;
$result[$k]['name'] =$names[$k];
$result[$k]['temp'] = $temps[0];
}
echo '<pre>'; print_r($result);
output:-
Array
(
[0] => Array
(
[id] => 53
[name] => fire
[temp] => 500
)
[1] => Array
(
[id] => 54
[name] => water
[temp] => 500
)
[2] => Array
(
[id] => 55
[name] => earth
[temp] => 500
)
)
If you are ok with a destructive solution, array_shift could do the trick :
$elements = array();
while (!empty($ids)) {
$elements[] = array(
'id' => array_shift($ids),
'name' => array_shift($names),
'temp' => array_shift($temps),
);
}
If you want to make a function, using the same arguments than your example, a solution could be
function atg($array) {
$elements = array();
while (!empty($array[0])) {
$new_element = array();
foreach ($array as $key_name => $array_to_shift) {
$new_element[$key_name] = array_shit($array_to_shift);
}
$elements[] = $new_element;
}
return $elements;
}
$result[$ids]['name'] = $names[0];
$result[$ids]['temp'] = $temps[0]

Storing value in unknown depth of multidimensional array

I have this exath path saved somewhere:
Array
(
[0] => library
[1] => 1
[2] => book
[3] => 0
[4] => title
[5] => 1
)
I have some array and I want to change the value on this index:
$values[library][1][book][0][title][1] = "new value";
I have no idea, how to do this, because there can be any (unknown) number of dimensions. Any hints?
It makes sense to create a function that does this, so:
function array_path_set(array & $array, array $path, $newValue) {
$aux =& $array;
foreach ($path as $key) {
if (isset($aux[$key])) {
$aux =& $aux[$key];
} else {
return false;
}
}
$aux = $newValue;
return true;
}
$values = array(
'library' => array(
1 => array(
'book' => array(
0 => array(
'title' => array(
1 => 'MAGIC VALUE!',
),
),
),
),
),
);
$path = array('library', 1, 'book', 0, 'title', 1);
$newValue = 'ANOTHER MAGIC VALUE!';
var_dump($values);
var_dump(array_path_set($values, $path, $newValue));
var_dump($values);
Try
foreach ($array as $val) {
$indexes .= "[$val]";
}
${'output'.$indexes} = 'something';
Or
$indexes = '';
foreach ($array as $val) {
$indexes .= "[$val]";
}
$output = 'values'.$indexes;
$$output = 'something';
Are you trying to provide a new value for the title of book 0 in library 1? If so, you will have to search for library "1", with book "0" and then change the value of the "title". So if all your values in the array have the same six entries, start with loc = 0 look at the values at loc+1 (library id), loc+3 (book id) and loc+5 .. change title) .. if not increment loc by 6 and continue searching.
[sounds like homework, so no code provided. Pardon me if I am wrong.]
Just in case you weren't aware of it, the key
$values["library"][1]["book"][0]["title"][1]
Is not the same as the array example in your post. Presuming the array is $values, it has five elements:
$values = Array
(
[0] => "library"
[1] => 1
[2] => "book"
[3] => 0
[4] => "title"
[5] => 1
)
$values[0] = "Library";
$values[1] = "1";
$values[2] = "book";
$values[3] = "0";
etc...
Is this array structure what you intended? If not, post back with a more complete structure so we can help.
Also, you need quotes around the strings - I have included for clarity
You could take a look at the following link for array_search. Some of the posters have included examples of a multidimensional array search and there are other examples. Do a google search on "multidimensional array search" and you will likely find a solution. If you need more direction, post back your details.
try this ->
$keys = array('0'=>'for','1'=>'test','2'=>'only');
$value='ok';
function addArrayPathWithValue($keys,$value,$array = array(),$current =
array())
{
$function = __FUNCTION__;
if (count($current)==0)
{
$keys = array_reverse($keys);
$current = $value;
}
if (count($keys)==0)
{
return $current;
}
$array[array_shift($keys)]=$current;
return $function($keys,$value,NULL,$array);
}
$array = addArrayPathWithValue($keys,$value);
print_r($array);
//output: Array ( [for] => Array ( [test] => Array ( [only] => ok ) ) )

How can I insert an item in the middle of an object in PHP?

If I have a stdClass object like the one below, which has three arrays with non-numeric keys, what's the best way to insert another item into the object between my-account and settings?
[menu1] => stdClass Object
(
[my-account] => Array
(
[title] => 'My Account'
)
[settings] => Array
(
[title] => 'Settings'
)
[payments] => Array
(
[title] => 'Payments'
)
)
Create your own class for this with functions to add and remove items and their attributes. Then use some functions such as #genesis provided.
There's no function to do this. You'll have to do your own and rebuild your object
http://sandbox.phpcode.eu/g/fba96/3
<?php
function insert_after($var, $key, $value, $after){
$new_object = array();
foreach((array) $var as $k => $v){
$new_object[$k] = $v;
if ($after == $k){
$new_object[$key] = $value;
}
}
$new_object = (object) $new_object;
return $new_object;
}
$var = insert_after($var, 'new dummy var between', array('title' => 'value'), 'my-account');
Add your item and then remove and re-add the items which ned to come after it:
$menu1->newitem = array('title'=>'Title');
$settings = $menu1->settings;
unset($menu1->settings);
$menu1->settings = $settings;
unset($settings);
unset($menu1->payments);
$menu1->payments = $payments;
unset($payments);

weird php array

my php array looks like this:
Array (
[0] => dummy
[1] => stdClass Object (
[aid] => 1
[atitle] => Ameya R. Kadam )
[2] => stdClass Object (
[aid] => 2
[atitle] => Amritpal Singh )
[3] => stdClass Object (
[aid] => 3
[atitle] => Anwar Syed )
[4] => stdClass Object (
[aid] => 4
[atitle] => Aratrika )
) )
now i want to echo the values inside [atitle].
to be specific i want to implode values of atitle into another variable.
how can i make it happen?
With PHP 5.3:
$result = array_map(function($element) { return $element->atitle; }, $array);
if you don't have 5.3 you have to make the anonymous function a regular one and provide the name as string.
Above I missed the part about the empty element, using this approach this could be solved using array_filter:
$array = array_filter($array, function($element) { return is_object($element); });
$result = array_map(function($element) { return $element->atitle; }, $array);
If you are crazy you could write this in one line ...
Your array is declared a bit like this :
(Well, you're probably, in your real case, getting your data from a database or something like that -- but this should be ok, here, to test)
$arr = array(
'dummy',
(object)array('aid' => 1, 'atitle' => 'Ameya R. Kadam'),
(object)array('aid' => 2, 'atitle' => 'Amritpal Singh'),
(object)array('aid' => 3, 'atitle' => 'Anwar Syed'),
(object)array('aid' => 4, 'atitle' => 'Aratrika'),
);
Which means you can extract all the titles to an array, looping over your initial array (excluding the first element, and using the atitle property of each object) :
$titles = array();
$num = count($arr);
for ($i=1 ; $i<$num ; $i++) {
$titles[] = $arr[$i]->atitle;
}
var_dump($titles);
This will get you an array like this one :
array
0 => string 'Ameya R. Kadam' (length=14)
1 => string 'Amritpal Singh' (length=14)
2 => string 'Anwar Syed' (length=10)
3 => string 'Aratrika' (length=8)
And you can now implode all this to a string :
echo implode(', ', $titles);
And you'll get :
Ameya R. Kadam, Amritpal Singh, Anwar Syed, Aratrika
foreach($array as $item){
if(is_object($item) && isset($item->atitle)){
echo $item->atitle;
}
}
to get them into an Array you'd just need to do:
$resultArray = array();
foreach($array as $item){
if(is_object($item) && isset($item->atitle)){
$resultArray[] = $item->atitle;
}
}
Then resultArray is an array of all the atitles
Then you can output as you'd wish
$output = implode(', ', $resultArray);
What you have there is an object.
You can access [atitle] via
$array[1]->atitle;
If you want to check for the existence of title before output, you could use:
// generates the title string from all found titles
$str = '';
foreach ($array AS $k => $v) {
if (isset($v->title)) {
$str .= $v->title;
}
}
echo $str;
If you wanted these in an array it's just a quick switch of storage methods:
// generates the title string from all found titles
$arr = array();
foreach ($array AS $k => $v) {
if (isset($v->title)) {
$arr[] = $v->title;
}
}
echo implode(', ', $arr);
stdClass requires you to use the pointer notation -> for referencing whereas arrays require you to reference them by index, i.e. [4]. You can reference these like:
$array[0]
$array[1]->aid
$array[1]->atitle
$array[2]->aid
$array[2]->atitle
// etc, etc.
$yourArray = array(); //array from above
$atitleArray = array();
foreach($yourArray as $obj){
if(is_object($obj)){
$atitleArray[] = $obj->aTitle;
}
}
seeing as how not every element of your array is an object, you'll need to check for that.

Categories