2

I have a PHP object that I need to change the key name of in a foreach loop:

stdClass Object
(
   [first-name] => NAME
   [last-name] => NAME
   [phone-number] => NUMBER
   ...
)

I need to replace the dash '-' to a underscore '_' within a foreach so it looks like this:

stdClass Object
(
   [first_name] => NAME
   [last_name] => NAME
   [phone_number] => NUMBER
   ...
)

I found this post: PHP - How to rename an object property? which does tell me how to do it, the problem is that when I use unset() inside of the foreach it unsets all of the keys instead of just the ones I want it to.

Here's my code:

foreach ($customer as $key => $value) {
    $key2 = str_replace('-', '_', $key);
    $customer->$key2 = $customer->$key;            
    unset($customer->$key);
}

which returns an empty object:

stdClass Object
(
)

How can I unset the original keys without affecting the new ones?

  • ``$customer = json_decode(str_replace('-', '_', json_encode($customer)));`` https://3v4l.org/aaXG0 | Good if you're sure that values don't contain dashes. :) – OMi Shah Nov 20 '22 at 16:57
  • @OMiShah: Well even if you're sure today, I would not suggest to run this on data which may change over time. OP clearly only wants to map the keys and side effects on data is part of the question already, therefore ... NO (, not sorry :) ). – hakre Nov 20 '22 at 17:18
  • @hakre: Yes, you're right, sir ;) – OMi Shah Nov 20 '22 at 17:19

2 Answers2

2

There is a simple trick you can do in general and that is only doing the operation if there is a change:

    $key2 = str_replace('-', '_', $key);
    if ($key2 !== $key) {
        $customer->$key2 = $customer->$key;            
        unset($customer->$key);
    }

You just do nothing if the operation has already been done (by the outcome).

Within a foreach this can be further "tweaked" by making use of the continue keyword and inversing the comparison:

foreach ($customer as $key => $value) {
    $key2 = str_replace('-', '_', $key);
    if ($key2 === $key) {
        continue; // nothing to do
    }
    $customer->$key2 = $customer->$key;            
    unset($customer->$key);
}

But at the end of the day you only don't want to delete which equals to always set (which works if you unset first and iterate on a clone):

foreach (clone $customer as $key => $value) {
    $key2 = str_replace('-', '_', $key);
    unset($customer->$key);
    $customer->$key2 = $value;
}

So choose your weapon, there are even more ways to skin this cat, this just for inspiration. At the end of the day the order of the operations (adding, deleting etc.) is the "key" point.

All examples on 3v4l.org.

hakre
  • 193,403
  • 52
  • 435
  • 836
  • Can you please test your code? It doesn't seem to work. – KIKO Software Nov 20 '22 at 16:44
  • @KIKOSoftware: That's unfortunate as its perhaps pseudo code continued from the question. Which one specifically you'd like to see under test? Just so that I can add an online example on 3v4l.org. – hakre Nov 20 '22 at 16:46
  • You can use my 3v4l example to test your code. – KIKO Software Nov 20 '22 at 16:48
  • Well you could have been not so shy with your comment and actually tell me the clone was missing. No need to turn the object into an array copy (unless you probably want to go the route to rename the array keys which PHP has some functionality for, would also be an option). – hakre Nov 20 '22 at 16:55
  • Yeah, sorry, I was working on an answer, and simply tried your code, without trying to understand why it wouldn't work. No point in answering if there is already a correct answer. – KIKO Software Nov 20 '22 at 16:57
  • 1
    @ChristopherAnsbaugh: It did before the edit, yes. That is because foreach'ing the same instance (the one that is modified while iterating, [SO reference Q&A](https://stackoverflow.com/q/10057671/367456)), a `clone` resolves this easily. It requires to unset first IMHO due to how `$customer->key` on the original may have gone away due to earlier iterations and str_replace resulting in a same key by accident. – hakre Nov 20 '22 at 16:58
  • 3
    Cloning is a smart idea. – KIKO Software Nov 20 '22 at 16:58
  • Well, tweaking too much, one often outsmarts oneself. Therefore I think the first variant is the best one because it has the largest take-away: do nothing if already renamed. – hakre Nov 20 '22 at 17:02
1

You code almost seems to work, but its a bad idea to work on an object you're looping through. The solution is get_object_vars() which can get the properties before you loop:

foreach (get_object_vars($customer) as $key => $value) {
    $key2 = str_replace('-', '_', $key);
    $customer->$key2 = $customer->$key;            
    unset($customer->$key);
}

See: https://3v4l.org/sfZq3

KIKO Software
  • 15,283
  • 3
  • 18
  • 33