4
\$\begingroup\$

This works, but do I have to make a new array, result, or is there a way to just insert the field I need into each $key of the received parameter $data?

Won't this way take up more memory needlessly?

function add_tax($data){
            $result = [];
            foreach($data as $product => $details) {
                $details['tax'] = 0;
                $result[$product] = $details;
                } 
            return $result;
          }
\$\endgroup\$

2 Answers 2

4
\$\begingroup\$

You could try something along these lines:

function add_tax($data){
        foreach($data as $product => $details) {
            $data[$product]['tax'] = 0;
        }
        return $data;
}

EDIT 1:

After a bit more researching on foreach I have come up with the following alternative:

function add_tax(&$data) {
    foreach($data as &$details)
        $details['tax'] = 0;
}

In reference to Alex L: I apologize, the original answer was rushed. The first snippet was simply a way of answering the question, that yes, it is possible to iterate through $data without generating a new variable. Also, to MikeiLL, yes, that would be a waste of memory. This second snippet passes both variables by reference, which does not require any excess usage of memory. It is the most optimal format of this process.

NOTE: The unset in the function is not exactly necessary, but as your question referenced memory usage, unset breaks the reference and releases that memory.

EDIT 2:

Due to very strong backing in the comments, I've removed the call to unset (truth be told I'd never use it myself, but wanted to reference it's usage within the function)

\$\endgroup\$
12
  • \$\begingroup\$ Ya know what would make your answer even better? If I knew why you changed what you did, and how that improves the original code! \$\endgroup\$
    – Alex L
    Commented Jul 17, 2014 at 17:36
  • 1
    \$\begingroup\$ You don't need the unset since $details goes out of scope immediately and is not reused. \$\endgroup\$ Commented Jul 17, 2014 at 18:52
  • 2
    \$\begingroup\$ unset() does just what it's name says - unset a variable. It does not force immediate memory freeing. PHP's garbage collector will do it when it see fits as unset() just decrements the refcount by one. If, and only if, the refcount reaches zero, then PHP knows no more symbols point to the zval, and will free the refcount automatically. \$\endgroup\$
    – jsanc623
    Commented Jul 17, 2014 at 19:34
  • 1
    \$\begingroup\$ Nope, because $data is passed as a reference, there's no need to return it. \$\endgroup\$ Commented Jul 17, 2014 at 19:38
  • 1
    \$\begingroup\$ @MikeiLL This is why I tend to favor functions without side-effects that return a copy of the data modified appropriately. Both have their uses, however. :) \$\endgroup\$ Commented Jul 17, 2014 at 22:23
3
\$\begingroup\$

PHP provides quite a few handy array-manipulation functions. This is a case for array_map which passes each value to a callback and assigns it to the same key in a new array.

function add_tax(array $data) {
    return array_map(function ($details) {
        $details['tax'] = 0;
        return $details;
    }, $data);
}

This version won't modify the original $data array passed to add_tax.

How does this differ from writing your own loop using foreach? Mostly in style. You'll pay a minor performance penalty switching between system and user space (once for each call to the callback), but you won't notice this unless $data is huge.

It looks much nicer when the callback returns an immediate expression instead of using one statement to modify and another to return. You can do this here using array_merge. In fact, this also allows you to refactor add_tax into a generic helper function.

function extend_arrays(array $arrays, array $additions) {
    return array_map(function ($array) use ($additions) {
        return array_merge($array, $additions);
    }, $arrays);
}

function add_tax($data) {
    return extend_arrays($data, array('tax' => 0));
}

function add_tax_and_shipping($data) {
    return extend_arrays($data, array('tax' => 0, 'shipping' => 0));
}
\$\endgroup\$
5
  • 3
    \$\begingroup\$ I'm actually extremely fond of this question now. One of the things I love about programming is how you can solve the same problem infinite numbers of ways. My example was an attempt at removing as much as I could, and giving the simplest version of a solution as I could. This is an example of a more robust, expandable solution. \$\endgroup\$ Commented Jul 17, 2014 at 19:40
  • \$\begingroup\$ This is beautiful. Learned about array maps in Eloquent Javascript. For some reason I'm getting Undefined variable: additions, though... \$\endgroup\$
    – MikeiLL
    Commented Jul 17, 2014 at 20:14
  • 1
    \$\begingroup\$ @MikeiLL Fixed, thanks! I was missing the use clause to give the callback access to $additions. Probably too much JavaScript lately which doesn't require such nonsense. \$\endgroup\$ Commented Jul 17, 2014 at 22:22
  • \$\begingroup\$ Ahhh. I spent about an hour trying to make the useless version work. :) Thank you, professor. \$\endgroup\$
    – MikeiLL
    Commented Jul 17, 2014 at 23:19
  • \$\begingroup\$ So in this case, would it have to be an anonymous function to work, and is the parameter referenced by use always pulled from an outer function? \$\endgroup\$
    – MikeiLL
    Commented Jul 18, 2014 at 0:42

Not the answer you're looking for? Browse other questions tagged or ask your own question.