4

Assume that you have a PHP array like this, coming from a mysqli query:

$array = (
    'user_id' => 1
    'user_name' => 'User',
    'user_email' => 'mail@user.com'
);

Now I would like to create an instance of my user class. The constructor takes an array but the keys should be 'id', 'name', and 'email'.

What is the fastest way to edit the array above so that I get a new array like this:

$newArray = (
    'id' => 1
    'name' => 'User',
    'email' => 'mail@user.com'
);

$user = new User($newArray);

Of course I could simply loop through the original $array and use preg_replace() like this:

$newArray = array();

foreach ($array as $key => $value) {
    $newKey = preg_replace('/^user_/', '', $key);
    $newArray[$newKey] = $value;
}

But there surely is a faster way who could solve this problem? At the moment I cannot think of a good way, that's how I ended up here.

andreas
  • 7,844
  • 9
  • 51
  • 72
  • 1
    "Faster" in which sense? What you have is reasonable, although the regex is probably overkill. – Jon Jan 16 '14 at 13:07
  • Everyone says for/foreach are quite slow in PHP and sometimes there are methods in PHP which are faster than custom looping (for example, array_walk etc.). Performance could be an issue for me because I create many user instances at runtime. – andreas Jan 16 '14 at 13:09
  • They are slow compared to built-ins, but there's a catch. Function calls are also slow in PHP, and `array_walk` & co will invoke your callback once per item. This may well end up being worse than `foreach`. In any case, it seems to me that the answer to your question is simply "measure it". – Jon Jan 16 '14 at 13:12
  • More generalized advice on changing keys: [Fastest way to add prefix to array keys?](https://stackoverflow.com/q/2607595/2943403) – mickmackusa May 14 '23 at 07:41

2 Answers2

3

If you're worried about performance, don't go doing trivial operations with regular expressions, they're inherently slow. Apart from that, since you're actively modifying keys, there's no other way than reconstructing a new array:

$params = [];
foreach($array as $key => $value)
  $params[substr($key, 5)] = $value;

In this code substr($key, 5) just strips the leading user_ part of each key and rebuilds the $params array to what you need. I'd be highly surprised if there was a faster solution.

If you actually want to check for data integrity and only extract the properly prefixed keys the problem becomes more complex:

function extractPrefixedElements(array $array, $prefix)
{
  $result = [];
  foreach($array as $key => $value)
  {
    list($pre, $newKey) = explode('_', $key, 2);
    if($pre == $prefix)
      $result[$newKey] = $value;
  }
  return $result;
}
extractPrefixedElements($array, 'user');
Niels Keurentjes
  • 41,402
  • 9
  • 98
  • 136
  • OK, and if I need to check if the string starts with "user_", would you suggest to check the substr($key, 5) against "user_" with an if clause? – andreas Jan 16 '14 at 13:33
  • Actually, I'd suggest modification: to declare some variable (which hold string to be replaced), like `$replace = 'user_'`, then calculate it's length, like `$l = strlen($replace);` - and then pass `substr($key, $l)` into your loop. That will be more common solution with only a bit bigger overhead – Alma Do Jan 16 '14 at 13:34
  • @andreas that's another question, this is the solution to "what is the fastest way to strip an identical lead string from every key in an array". If you have to check for data integrity other factors come into play. – Niels Keurentjes Jan 16 '14 at 13:39
  • @andreas added a solution for matching required prefixes. `explode` is actually rather efficient under the hood so this should be reasonably fast. – Niels Keurentjes Jan 16 '14 at 14:04
3

Surely you don't need preg_replace() here, because you're in the loop and regular expressions aren't always fast (because you initialize the whole engine on each iteration)

There's only one way you can do that - you should just replace user_ anywhere you encounter when iterating on array keys.

function prepare(array $array) {

   $result = array();

   foreach ($array as $key => $value) {
       $key = str_replace('user_', '', $key);

       $result[$key] = $value;
   }

   return $result;
}

// And finally
$newArray = prepare($array);
Yang
  • 8,580
  • 8
  • 33
  • 58
  • Why is this answer, posted 6 minutes after mine and essentially identical, but **slower** because `str_replace` is fundamentally slower than `substr` for this case, receiving more upvotes? – Niels Keurentjes Jan 16 '14 at 13:28
  • @NielsKeurentjes I have no idea. It's even not correct cause will fail on key like `some_user_field` – Alma Do Jan 16 '14 at 13:29
  • @AlmaDo who said it can be `some_user_field`? or anything else? that would be another solution – Yang Jan 16 '14 at 13:31
  • @djay who said it cannot be? That's why in general it's good to try to predict all cases (and all corner cases especially) – Alma Do Jan 16 '14 at 13:31
  • @djay or `user_created_by_user_id` would also break magnificently, and is quite probable – Niels Keurentjes Jan 16 '14 at 13:32
  • @NielsKeurentjes sure this answer is absolutely 100% identical to yours – Yang Jan 16 '14 at 13:33
  • No, as said, `str_replace` inherently is computationally more expensive since it has to scan the string, and expand it an unknown number of times, instead of just constructing a new one from an indexed offset. – Niels Keurentjes Jan 16 '14 at 13:34
  • @NielsKeurentjes citation needed – Yang Jan 16 '14 at 13:35
  • This answer cannot be relied upon for general use because: 1. It does not limit the number of replacements that can occur on a given key string and 2. It makes no attempt to verify that the replacement occurs at the start of the key string. `preg_replace()` has the ability to overcome both of these hurdles. – mickmackusa Apr 20 '22 at 22:51