remove duplicates from an array based on object property continued - php

related to : Remove duplicates from an array based on object property?
is there a way to do this exact same thing but consider the order? for example if instead of keeping [2] I wanted to keep [1]
[0]=>
object(stdClass)#337 (9) {
["term_id"]=>
string(2) "23"
["name"]=>
string(12) "Assasination"
["slug"]=>
string(12) "assasination"
}
[1]=>
object(stdClass)#44 (9) {
["term_id"]=>
string(2) "14"
["name"]=>
string(16) "Campaign Finance"
["slug"]=>
string(16) "campaign-finance"
}
[2]=>
object(stdClass)#298 (9) {
["term_id"]=>
string(2) "15"
["name"]=>
string(16) "Campaign Finance"
["slug"]=>
string(49) "campaign-finance-good-government-political-reform"
}
I tried posting a comment but because of my reputation it won't let me so I decided to start a new thread

It is trivial to reverse and array. So before processing the array, call array_reverse() on it:
/** flip it to keep the last one instead of the first one **/
$array = array_reverse($array);
Then at the end you can reverse it again if ordering is an issue:
/** Answer Code ends here **/
/** flip it back now to get the original order **/
$array = array_reverse($array);
So put all together looks like this:
class my_obj
{
public $term_id;
public $name;
public $slug;
public function __construct($i, $n, $s)
{
$this->term_id = $i;
$this->name = $n;
$this->slug = $s;
}
}
$objA = new my_obj(23, "Assasination", "assasination");
$objB = new my_obj(14, "Campaign Finance", "campaign-finance");
$objC = new my_obj(15, "Campaign Finance", "campaign-finance-good-government-political-reform");
$array = array($objA, $objB, $objC);
echo "Original array:\n";
print_r($array);
/** flip it to keep the last one instead of the first one **/
$array = array_reverse($array);
/** Answer Code begins here **/
// Build temporary array for array_unique
$tmp = array();
foreach($array as $k => $v)
$tmp[$k] = $v->name;
// Find duplicates in temporary array
$tmp = array_unique($tmp);
// Remove the duplicates from original array
foreach($array as $k => $v) {
if (!array_key_exists($k, $tmp))
unset($array[$k]);
}
/** Answer Code ends here **/
/** flip it back now to get the original order **/
$array = array_reverse($array);
echo "After removing duplicates\n";
echo "<pre>".print_r($array, 1);
produces the following output
Array
(
[0] => my_obj Object
(
[term_id] => 23
[name] => Assasination
[slug] => assasination
)
[1] => my_obj Object
(
[term_id] => 15
[name] => Campaign Finance
[slug] => campaign-finance-good-government-political-reform
)
)

Here is one way to do it. This should keep the first object for each name attribute and remove the rest.
$unique = [];
foreach ($array as $key => $object) {
if (!isset($unique[$object->name])) {
// this will add each name to the $unique array the first time it is encountered
$unique[$object->name] = true;
} else {
// this will remove all subsequent objects with that name attribute
unset($array[$key]);
}
}
I used the object name as a key rather than a value in the $unique array so isset could be used to check for existing names, which should be faster than in_array which would have to be used if the names were added as values.

You can do it with array_reduce:
$names = [];
$withUniqueNames = array_reduce(
[$objA, $objB, $objC],
function ($withUniqueNames, $object) use (&$names) {
if (!in_array($object->name, $names)) {
$names[] = $object->name;
$withUniqueNames[] = $object;
}
return $withUniqueNames;
},
[]
);
Basically, we traverse an array of objects and keep track of used names. If the name was not used yet, we add it to used and add current object to the result.
Here is working demo.

Related

Generate multidimensional array based on given array

I have an array say,
$arr = ["x", "y", "z"];
What I want to achieve is create another array based on given array such as
$arr1["x" =>["y" => ["z"]]] = "some value";
Any idea to achieve this? Thanks in advance.
Edited:
'some value' is just a dummy data. What I'm trying to achieve is the multidimensional structure.
You can recursively build an array, taking and removing the first element of an array on each call :
function buildArray($arr, $someValue)
{
if (count($arr) == 0)
return $someValue;
// the key is the first element of the array,
// removed and returned at the same time using array_shift()
return [ array_shift($arr) => buildArray($arr, $someValue) ];
}
$arr = ["x", "y", "z"];
$arr1 = buildArray($arr, "some value");
var_dump($arr1);
echo "------------------------" . PHP_EOL;
// note that $arr is preserved
var_dump($arr);
This outputs :
array(1) {
["x"]=>
array(1) {
["y"]=>
array(1) {
["z"]=>
string(10) "some value"
}
}
}
------------------------
array(3) {
[0]=>
string(1) "x"
[1]=>
string(1) "y"
[2]=>
string(1) "z"
}
$keys = array('key1', 'key2', 'key3');
$value = 'some value';
$md = array();
$md[$keys[count($keys)-1]] = $value;
for($i=count($keys)-2; $i>-1; $i--)
{
$md[$keys[$i]] = $md;
unset($md[$keys[$i+1]]);
}
print_r($md);
You need to create recursive function:
function rec_arr($ar, $val){
$res = [];
if(is_array($ar) && count($ar)>0){
$tmp = $ar[0]; // catching the first value
unset($ar[0]); // unset first value from given array
sort($ar); // makes indexes as 0,1,...
$res[$tmp] = rec_arr($ar, $val); // recursion
} else {
return $val; // passing value to the last element
}
return $res;
}
Demo
Outputs:
Array
(
[x] => Array
(
[y] => Array
(
[z] => some value
)
)
)

update a global array within a function

I have a global array in which I am trying to update a value (remain persistent) and then display it.
I get no error but the array never gets updated.
<?php
$anchor = 'bird';
$stuff = array('apple', 'bird', 'frog');
function insert($anchor, $stuff){
foreach($stuff as $part){
$new_array = array($anchor => rand());
if($part == $anchor){
array_push($stuff, $new_array);
}
}
}
for($x = 0; $x < 2; $x++){
insert($anchor, $stuff);
var_dump($stuff);
}
output:
array(3) {
[0]=>
string(5) "apple"
[1]=>
string(4) "bird"
[2]=>
string(4) "frog"
}
array(3) {
[0]=>
string(5) "apple"
[1]=>
string(4) "bird"
[2]=>
string(4) "frog"
}
expected output:
{'bird' => 674568765}
{'bird' => 986266261}
How do I update an array within a function, so that the changes are reflected globally (persistent)?
Pass the variable $stuff by reference. Note the & in the function parameters.
function insert($anchor, &$stuff){ // note the & mark
foreach($stuff as $part){
$new_array = array($anchor => rand());
if($part == $anchor){
array_push($stuff, $new_array);
}
}
}
As others have mentioned: by default, PHP function arguments are passed by value, meaning that a value changed inside a function does not change outside of the function.
I suggest returning the new $stuff value from your function:
<?php
$anchor = 'bird';
$stuff = array('apple', 'bird', 'frog');
function insert($anchor, $stuff){
foreach($stuff as $part){
$new_array = array($anchor => rand());
if($part == $anchor){
array_push($stuff, $new_array);
}
}
return $stuff;
}
for($x = 0; $x < 2; $x++){
$stuff=insert($anchor, $stuff);
}
echo"<pre>".print_r($stuff,true)."</pre>";
?>
Array
(
[0] => apple
[1] => bird
[2] => frog
[3] => Array
(
[bird] => 618490127
)
[4] => Array
(
[bird] => 1869073273
)
)
Other solutions propose passing by reference, to which I'm not opposed. But I have definitely run into buggy code in which it wasn't immediately clear that a function was changing a value, and I've been confused by unexpected loop behavior. As a result, I generally prefer arguably more readable/maintainable code that returns a new value.
See When to pass-by-reference in PHP.
Get technical with Sara Golemon.
If you want changes to a variable that you pass to a function to persist after the function ends, pass the variable by reference:
Change:
function insert($anchor, $stuff)
To
function insert($anchor, &$stuff)

Php array get all values that are repeating

I have a Php array that have values of times as array values and timestamps as key array is like this:
array(
144454884=>"12:00am", 145454884=>"12:30am", 144474884=>"1:00am", 144454864=>"1:30am", 143354884=>"1:00am", 144654884=>"1:30am", 1444567584=>"2:00am "
);
Timestamp values in above example are not real I wrote an example they are useless anyway unless your timezone matches mine.
Problem:
I need to get "1:00am" and "1:30am" twice I can get repeating values 1 time as shown in answer here:
php return only duplicated entries from an array
I need both repeating values two times with both keys and values being repeated because I need to eliminate those timestamps from week time on my system because of daylight saving a time is repeating and I don't want to show 1:00am at all I just want to show this time as unavailable.
I am not 100% sure what you wanted but this is what I think you need.
Assuming your input array is called $a
$b = array_flip(array_flip($a));
$c = array_diff_key($a, $b);
$b will contain an array of unique values.
$c will contain the elements that were removed.
Results of $b and $c are as follows:
array(5) {
[144454884] = string(7) "12:00am"
[145454884] = string(7) "12:30am"
[143354884] = string(6) "1:00am"
[144654884] = string(6) "1:30am"
[1444567584] = string(7) "2:00am "
}
array(2) {
[144474884] = string(6) "1:00am"
[144454864] = string(6) "1:30am"
}
This code works :
<?php
$array_new = [];
$array_tmp = [];
$array = array(1=>'1233',2=>'12334',3 =>'Hello' ,4=>'hello', 5=>'U');
//loop trough all elements in array and for ever element create key
//For "world" key is "world"
//For "World" key is "world"
//For "WORLD" key is "world"
//So all this cases have same key and differenet representation eg. "world" => ["world","World","WORLD"]
foreach($array as $k => $v){
$index = strtolower($v);
$array_tmp[$index][] = $v;
}
//loop trough new array with new keys and if there are more than one element(> 1) for some key, all of his representations put in new array
foreach($array_tmp as $k => $v){
if(count($v) > 1){
foreach($v as $k2 => $v2){
$array_new[] = $v2;
}
}
}
echo '<pre>';
print_r($array_new);
echo '<pre>';
A possible solution keeping the key information (I will assign the intermediate results to their own variables otherwise it can be confusing to read)
$array = array(
143354883 => "1:00am",
144454884 => "12:00am",
145454884 => "12:30am",
144474884 => "1:00am",
144454864 => "1:30am",
143354884 => "1:00am",
144654884 => "1:30am",
1444567584 => "2:00am ",
0 => 4,
1 => 4,
2 => 4,
3 => "Test",
4 => "TEST",
5 => "test "
);
// Used this array_iunique function: http://stackoverflow.com/questions/2276349/case-insensitive-array-unique
function array_iunique($array) {
return array_intersect_key(
$array,
array_unique(array_map("StrToLower",$array))
);
}
$unique = array_iunique($array);
// Then get the difference by key, that will give you all the duplicate values:
$diff_key = array_diff_key($array, $unique);
// Now we have to find the values that are in the $diff_key and the $unique because we also want to have those:
$correspondingValues = array_uintersect($unique, $diff_key, "strcasecmp");
// Then we have to combine the $duplicate values with the $diff_key and preserve the keys:
$result = array_replace($correspondingValues, $diff_key);
var_dump($result);
Will result in:
array(10) {
[143354883]=>
string(6) "1:00am"
[144454864]=>
string(6) "1:30am"
[0]=>
int(4)
[3]=>
string(4) "Test"
[144474884]=>
string(6) "1:00am"
[143354884]=>
string(6) "1:00am"
[144654884]=>
string(6) "1:30am"
[1]=>
int(4)
[2]=>
int(4)
[4]=>
string(4) "TEST"
}

PHP Array Rename Dynamic Array Keys

I am needing to rename dynamic array keys, and create a new array.
Here is the array as given:
array(21)
{
["0161"] =>
array(5)
{
["L_NAME0161"] =>
string(13) "john%20Hewett"
["L_TRANSACTIONID0161"] =>
string(17) "50350073XN1446019"
["L_AMT0161"] =>
string(6) "8%2e50"
["L_FEEAMT0161"] =>
string(9) "%2d0%2e55"
["L_NETAMT0161"] =>
string(6) "7%2e95"
}
["08591"] =>
array(5)
{
["L_NAME08591"] =>
string(18) "Norbert%20Bendixen"
["L_TRANSACTIONID08591"] =>
string(17) "1WN98871MS4263823"
["L_AMT08591"] =>
string(6) "8%2e50"
["L_FEEAMT08591"] =>
string(9) "%2d0%2e55"
["L_NETAMT08591"] =>
string(6) "7%2e95"
}
}
Here is the code I am using which is not working for me:
foreach ($reb AS $newrebarray)
{
foreach ($newrebarray as $ke => $val)
{
if (preg_match("/L_NETAMT/i", $ke))
{
$newarrayreb1 = array('Net' => $val);
}
if (preg_match("/L_TRANSACTIONID/i", $ke))
{
$newarrayreb1 = array('TransactID' => $val);
}
if (preg_match("/L_NAME/i", $ke))
{
$newarrayreb1 = array('Name' => $val);
}
}
}
notice that the array keys are dynamic, I want to create a new array with static keys, and the associated values. When I run the code, I get five different arrays.
First I would define a function that does replacement based on a captured memory segment of a regular expression:
function do_translate($match)
{
switch ($match[1]) {
case 'L_NAME':
return 'Name';
case 'L_NETAMT':
return 'Net';
case 'L_TRANSACTIONID':
return 'TransactID';
}
// in all other cases, return the full match
return $match[0];
}
Then iterate over the blocks, send the array keys through a translation pass and then recombine the new keys with the existing values:
foreach ($reb as $id => $data) {
$new_keys = preg_replace_callback('/^(L_[A-Z]+)' . preg_quote($id) . '$/i', 'do_translate', array_keys($data));
// create the new array with translated keys
$reb[$id] = array_combine($new_keys, $data);
}
I noticed that the array keys were a combination of the field and the product id (I guess), so I've used that knowledge to strengthen the regular expression
Not tested, haven't fully woken up yet, so this'll probably kick your dog and delete all your savegames:
$translations = array(
'L_TRANSACTIONID' => 'Translation',
'L_NAME' => 'Name',
'L_NETAMT' => 'Net'
);
foreach($array as $parentkey => $subarray) {
foreach($subarray as $subkey => $val) {
if (preg_match('/^(L_.*?)\d*$/', $matches)) {
$newKey = $translations[$matches[1]];
$array[$parentkey][$newkey] = $val;
unset($array[$parentkey][$subkey]);
}
}
}

Turn flat array into nested array

Just working on something and can't find a simple solution to this problem without just looping through the array with a foreach. Does anyone have a simple solution for this
I want to turn this
&array(4) {
["a"]=>int(0)
["b"]=>int(1)
["c"]=>int(2)
["d"]=>int(3)
}
Into this
array(1) {
["a"]=>
array(1) {
[0]=>
array(1) {
["b"]=>
array(1) {
[0]=>
array(1) {
["c"]=>
array(1) {
[0]=>
array(1) {
["d"]=> int(1) //this value does not matter
}
}
}
}
}
}
}
The values don't matter at all I just need the keys to run against a array_intersect_key_recursive function that I have.
EDIT : The array has changed after testing it needs to be nested as an array of an array
I don't know how this could possibly end up helping you, but it was a fun exercise nonetheless.
$newArray = array();
$last = &$newArray;
$array = array_reverse(array_keys($array));
while ($item = array_pop($array)) {
if (!is_array($last)) {
$last = array();
}
$last[$item] = array(array());
$last = &$last[$item][0];
}
NOTE: I made this answer with the int(1). You said the value is not important so I'm not going to bother changing it for now, but you would have to do a bit more work if the value was important (probably something like get the value from the original array with $item as the key).
Another approach using recursion:
function toNestedArray(array $array, $index = 0) {
$return = array();
if ($index < count($array)) {
$return[$array[$index]] = toNestedArray($array, ++$index);
}
return $return;
}
Usage Example:
$flatArray = array('a' => 1, 'b' => 2, 'c' => 3, 'd' => 4);
$nestedArray = toNestedArray(array_keys($flatArray));
print_r($nestedArray);
Output:
Array
(
[a] => Array
(
[b] => Array
(
[c] => Array
(
[d] => Array
(
)
)
)
)
)

Categories