PHP Rename object property while maintaining its position - php

How can I rename an object property without changing the order?
Example object:
$obj = new stdClass();
$obj->k1 = 'k1';
$obj->k2 = 'k2';
$obj->k3 = 'k3';
$obj->k4 = 'k4';
Example rename by creating new property:
$obj->replace = $obj->k3;
unset($obj->k3);
Result:
( k1=>k1, k2=>k2, k4=>k4, replace=>k3 )
See the problem?
Simply recreating the object property causes the order to change.
I had a similar issue with arrays and came up with this solution:
Implemented solution for arrays
function replaceKey(&$array, $find, $replace)
{
if(array_key_exists($find, $array)){
$keys = array_keys($array);
$i = 0;
$index = false;
foreach($array as $k => $v){
if($find === $k){
$index = $i;
break;
}
$i++;
}
if ($index !== false) {
$keys[$i] = $replace;
$array = array_combine($keys, $array);
}
}
}
Looking for something similar to this but a function that will work on objects.
Can't seem to find any documentation on object property position indexes.

So, readd your properties
$obj = new YourClass();
$backup = [];
foreach ($obj as $key => $value)
{
$backup[$key] = $value;
unset($obj->$key);
}
replaceKey($backup, $find, $replace);
foreach ($backup as $key => $value)
{
$obj->$key = $value;
}

Since you have a function for your arrays, why dont you do something like :
$array = (array) $obj;
$replacedKeyArray = replaceKey($array);
$newObject = (object) $replacedKeyArray;

Related

PHP cant add new object keys in foreach loop

I want to create new object keys in foreach loop so I write:
$all_variants = $p->variants;
foreach ($all_variants as $a) {
$a->discount_price = $a->price;
$a->original_price = $a->price;
$a->button_text = 'BUY';
}
but this code wont create new keys (discount_price, original_price, button_text) ... $all_variants->discount_price is undefinded ....
Whats bad in my code?
You can use next trick for this
$all_variants = $p->variants;
foreach ($all_variants as &$a) {
$tmp = (array)$a;
$tmp['discount_price'] = $tmp['price'];
$tmp['original_price'] = $tmp['price'];
$tmp['button_text'] = 'BUY';
$a = (object)$tmp;
}
Here object converted to array modified and converted to object again
php code online test
This works for me:
foreach ($p->variants as $a) {
$a['discount_price'] = $a['price'];
$a['original_price'] = $a['price'];
$a->button_text = 'BUY';
$all_variants[] = $a;
}
You can modify the original array, but not the copy of the element you get in the loop...
foreach($p->variants as $i => $a) {
// $p->variants[ $i ] corresponds to $a
$p->variants[ $i ]['discount_price'] = $a['price'];
}

PHP: Set object properties inside a foreach loop

Is it possible to set property values of an object using a foreach loop?
I mean something equivalent to:
foreach($array as $key=>$value) {
$array[$key] = get_new_value();
}
EDIT: My example code did nothing, as #YonatanNir and #gandra404 pointed out, so I changed it a little bit so it reflects what I meant
You can loop on an array containing properties names and values to set.
For instance, an object which has properties "$var1", "$var2", and "$var3", you can set them this way :
$propertiesToSet = array("var1" => "test value 1",
"var2" => "test value 2",
"var3" => "test value 3");
$myObject = new MyClass();
foreach($propertiesToSet as $property => $value) {
// same as $myObject->var1 = "test value 1";
$myObject->$property = $value;
}
Would this example help at all?
$object = new stdClass;
$object->prop1 = 1;
$object->prop2 = 2;
foreach ($object as $prop=>$value) {
$object->$prop = $object->$prop +1;
}
print_r($object);
This should output:
stdClass Object
(
[prop1] => 2
[prop2] => 3
)
Also, you can do
$object = new stdClass;
$object->prop1 = 1;
$object->prop2 = 2;
foreach ($object as $prop=>&$value) {
$value = $value + 1;
}
print_r($object);
You can implement Iterator interface and loop through the array of objects:
foreach ($objects as $object) {
$object->your_property = get_new_value();
}
Hacked away at this for a few hours and this is what i finally used. Note the parameters passed by reference in two places. One when you enter the method and the other in the foreach loop.
private function encryptIdsFromData(&$data){
if($data == null)
return;
foreach($data as &$item){
if(isset($item["id"]))
$item["id"] = $this->encrypt($item["id"]);
if(is_array($item))
$this->encryptIdsFromData($item);
}
}

PHP Can't get the right format for array

I got stuck somehow on the following problem:
What I want to achieve is to merge the following arrays based on key :
{"Entities":{"submenu_id":"Parents","submenu_label":"parents"}}
{"Entities":{"submenu_id":"Insurers","submenu_label":"insurers"}}
{"Users":{"submenu_id":"New roles","submenu_label":"newrole"}}
{"Users":{"submenu_id":"User - roles","submenu_label":"user_roles"}}
{"Users":{"submenu_id":"Roles - permissions","submenu_label":"roles_permissions"}}
{"Accounting":{"submenu_id":"Input accounting data","submenu_label":"new_accounting"}}
Which needs to output like this:
[{"item_header":"Entities"},
{"list_items" :
[{"submenu_id":"Parents","submenu_label":"parents"},
{"submenu_id":"Insurers","submenu_label":"insurers"}]
}]
[{"item_header":"Users"},
{"list_items" :
[{"submenu_id":"New roles","submenu_label":"newrole"}
{"submenu_id":"User - roles","submenu_label":"user_roles"}
{"submenu_id":"Roles - permissions","submenu_label":"roles_permissions"}]
}]
[{"item_header":"Accounting"},
{"list_items" :
[{"submenu_id":"Input accounting data","submenu_label":"new_accounting"}]
}]
I have been trying all kinds of things for the last two hours, but each attempt returned a different format as the one required and thus failed miserably. Somehow, I couldn't figure it out.
Do you have a construction in mind to get this job done?
I would be very interested to hear your approach on the matter.
Thanks.
$input = array(
'{"Entities":{"submenu_id":"Parents","submenu_label":"parents"}}',
'{"Entities":{"submenu_id":"Insurers","submenu_label":"insurers"}}',
'{"Users":{"submenu_id":"New roles","submenu_label":"newrole"}}',
'{"Users":{"submenu_id":"User - roles","submenu_label":"user_roles"}}',
'{"Users":{"submenu_id":"Roles - permissions","submenu_label":"roles_permissions"}}',
'{"Accounting":{"submenu_id":"Input accounting data","submenu_label":"new_accounting"}}',
);
$input = array_map(function ($e) { return json_decode($e, true); }, $input);
$result = array();
$indexMap = array();
foreach ($input as $index => $values) {
foreach ($values as $k => $value) {
$index = isset($indexMap[$k]) ? $indexMap[$k] : $index;
if (!isset($result[$index]['item_header'])) {
$result[$index]['item_header'] = $k;
$indexMap[$k] = $index;
}
$result[$index]['list_items'][] = $value;
}
}
echo json_encode($result);
Here you are!
In this case, first I added all arrays into one array for processing.
I thought they are in same array first, but now I realize they aren't.
Just make an empty $array=[] then and then add them all in $array[]=$a1, $array[]=$a2, etc...
$array = '[{"Entities":{"submenu_id":"Parents","submenu_label":"parents"}},
{"Entities":{"submenu_id":"Insurers","submenu_label":"insurers"}},
{"Users":{"submenu_id":"New roles","submenu_label":"newrole"}},
{"Users":{"submenu_id":"User - roles","submenu_label":"user_roles"}},
{"Users":{"submenu_id":"Roles - permissions","submenu_label":"roles_permissions"}},
{"Accounting":{"submenu_id":"Input accounting data","submenu_label":"new_accounting"}}]';
$array = json_decode($array, true);
$intermediate = []; // 1st step
foreach($array as $a)
{
$keys = array_keys($a);
$key = $keys[0]; // say, "Entities" or "Users"
$intermediate[$key] []= $a[$key];
}
$result = []; // 2nd step
foreach($intermediate as $key=>$a)
{
$entry = ["item_header" => $key, "list_items" => [] ];
foreach($a as $item) $entry["list_items"] []= $item;
$result []= $entry;
}
print_r($result);
I would prefer an OO approach for that.
First an object for the list_item:
{"submenu_id":"Parents","submenu_label":"parents"}
Second an object for the item_header:
{"item_header":"Entities", "list_items" : <array of list_item> }
Last an object or an array for all:
{ "Menus: <array of item_header> }
And the according getter/setter etc.
The following code will give you the requisite array over which you can iterate to get the desired output.
$final_array = array();
foreach($array as $value) { //assuming that the original arrays are stored inside another array. You can replace the iterator over the array to an iterator over input from file
$key = /*Extract the key from the string ($value)*/
$existing_array_for_key = $final_array[$key];
if(!array_key_exists ($key , $final_array)) {
$existing_array_for_key = array();
}
$existing_array_for_key[count($existing_array_for_key)+1] = /*Extract value from the String ($value)*/
$final_array[$key] = $existing_array_for_key;
}

Split an array into sub arrays

I would like to split an array:
$o = json_decode('[{"id":"1","color":"green"},{"id":"2","color":"green"},{"id":"3","color":"yellow"},{"id":"4","color":"green"}]');
based on the color attribute of each item, and fill corresponding sub arrays
$a = array("green", "yellow", "blue");
function isGreen($var){
return($var->color == "green");
}
$greens = array_filter($o, "isGreen");
$yellows = array_filter($o, "isYellow");
// and all possible categories in $a..
my $a has a length > 20, and could increase more, so I need a general way instead of writing functions by hand
There doesn't seem to exist a function array_split to generate all filtered arrays
or else I need a sort of lambda function maybe
You could do something like:
$o = json_decode('[{"id":"1","color":"green"},{"id":"2","color":"green"},{"id":"3","color":"yellow"},{"id":"4","color":"green"}]');
$greens = array_filter($o, function($item) {
if ($item->color == 'green') {
return true;
}
return false;
});
Or if you want to create something really generic you could do something like the following:
function filterArray($array, $type, $value)
{
$result = array();
foreach($array as $item) {
if ($item->{$type} == $value) {
$result[] = $item;
}
}
return $result;
}
$o = json_decode('[{"id":"1","color":"green"},{"id":"2","color":"green"},{"id":"3","color":"yellow"},{"id":"4","color":"green"}]');
$greens = filterArray($o, 'color', 'green');
$yellows = filterArray($o, 'color', 'yellow');
In my second example you could just pass the array and tell the function what to filter (e.g. color or some other future property) on based on what value.
Note that I have not done any error checking whether properties really exist
I would not go down the road of creating a ton of functions, manually or dynamically.
Here's my idea, and the design could be modified so filters are chainable:
<?php
class ItemsFilter
{
protected $items = array();
public function __construct($items) {
$this->items = $items;
}
public function byColor($color)
{
$items = array();
foreach ($this->items as $item) {
// I don't like this: I would prefer each item was an object and had getColor()
if (empty($item->color) || $item->color != $color)
continue;
$items[] = $item;
}
return $items;
}
}
$items = json_decode('[{"id":"1","color":"green"},{"id":"2","color":"green"},{"id":"3","color":"yellow"},{"id":"4","color":"green"}]');
$filter = new ItemsFilter($items);
$greens = $filter->byColor('green');
echo '<pre>';
print_r($greens);
echo '</pre>';
If you need more arguments you could use this function:
function splitArray($array, $params) {
$result = array();
foreach ($array as $item) {
$status = true;
foreach ($params as $key => $value) {
if ($item[$key] != $value) {
$status = false;
continue;
}
}
if ($status == true) {
$result[] = $item;
}
}
return $result;
}
$greensAndID1 = splitArray($o, array('color' => 'green', 'id' => 1));

Setting a value in a multi-dimensional array using an array of keys

In relation to this question i asked earlier: Searching multi-dimensional array's keys using a another array
I'd like a way to set a value in a multi-dimensional array (up to 6 levels deep), using a seperate array containing the keys to use.
e.g.
$keys = Array ('A', 'A2', 'A22', 'A221');
$cats[A][A2][A22][A221] = $val;
I tried writing a clumsy switch with little success... is there a better solution?
function set_catid(&$cats, $keys, $val) {
switch (count($keys)) {
case 1: $cats[$keys[0]]=$val; break;
case 2: $cats[$keys[0]][$keys[1]]=$val; break;
case 3: $cats[$keys[0]][$keys[1]][$keys[2]]=$val; break;
etc...
}
}
try this:
function set_catid(&$cats, $keys, $val) {
$ref =& $cats;
foreach ($keys as $key) {
if (!is_array($ref[$key])) {
$ref[$key] = array();
}
$ref =& $ref[$key];
}
$ref = $val;
}
function insertValueByPath($array, $path, $value) {
$current = &$array;
foreach (explode('/', $path) as $part) {
$current = &$current[$part];
}
$current = $value;
return $array;
}
$array = insertValueByPath($array, 'A/B/C', 'D');
// => $array['A']['B']['C'] = 'D';
You can obviously also use an array for $path by just dropping the explode call.
You should use references.
In the foreach we're moving deeper from key to key. Var $temp is reference to current element of array $cat. In the end temp is element that we need.
<?php
function set_catid(&$cats, $keys, $val) {
$temp = &$cats;
foreach($keys as $key) {
$temp = &$temp[$key];
}
$temp = $val;
}
$cats = array();
$keys = Array ('A', 'A2', 'A22', 'A221');
set_catid($cats, $keys, 'test');
print_r($cats);
?>

Categories