0

I'm looking for a way to replace nested foreach loops with a functional programming approach. Here is the sample data:

$mode[1] = [1, 2, 5, 6];
$mode[0] = [3, 4, 7, 8];

Currently my code is this:

foreach ($mode as $key => $value):
  foreach ($value as $v):
    $modes[$v] = $key;
  endforeach;
endforeach;

echo '<pre>' . print_r($modes, 1) . '</pre>';

This generates the desired output (which can be thought of as flipping a 2d array):

array (
  1 => 1,
  2 => 1,
  5 => 1,
  6 => 1,
  3 => 0,
  4 => 0,
  7 => 0,
  8 => 0,
)

Does anyone know how the foreach code block can be replaced with a functional programming alternative?

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
knot22
  • 2,648
  • 5
  • 31
  • 51

4 Answers4

2

You can do it with array_map()

array_map(function($key, $value) use (&$modes) {
    array_map(function($v) use (&$modes) {
        $modes[$v] = $key;
    }, $value);
}, array_keys($mode), array_values($mode));

I'm not sure why you would want to do this, the foreach version seems much clearer to me.

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • It is functional, but I agree with @Barmar that the `foreach` looks clearer. – Marijke Luttekes Aug 09 '17 at 11:44
  • This isn't functional at all since `&$modes` creates side effects and since you discard the return value of the outer/inner `map`. –  Aug 09 '17 at 13:20
  • @ftor I think he intended "functional" to mean "by calling higher-order functions", not in the side effect-free sense of pure functional programming. – Barmar Aug 09 '17 at 13:22
  • Well, now the OP thinks FP leads to laborious and hard to follow code without any advantages being obtained. This isn't true for pure functional code. Anayway, maybe the FP tag should be replaced with higher-order-functions. –  Aug 09 '17 at 13:42
1

I think array_walk() partnered with a Union Operator and array_fill_keys() seems like a clean choice for this task:

Code: (Demo)

$mode[1] = [1, 2, 5, 6];
$mode[0] = [3, 4, 7, 8];

$result = []; // declare empty array so that union operator works
array_walk($mode, function($a, $k) use (&$result) { $result += array_fill_keys($a, $k); });
var_export($result);

To avoid declaring any variables in the global scope, call array_reduce() on the first level keys and use those keys to access the second level subarray. (Demo)

var_export(
    array_reduce(
        array_keys($mode),
        fn($result, $k) => $result += array_fill_keys($mode[$k], $k),
        []
    )
);

Output (from either snippet):

array (
  1 => 1,
  2 => 1,
  5 => 1,
  6 => 1,
  3 => 0,
  4 => 0,
  7 => 0,
  8 => 0,
)
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
0

I'd use array_fill_keys to create the mode arrays, then merge them into one:

$modes = array_fill_keys($mode[1], 1) + array_fill_keys($mode[0], 0);

For legibility, you can split it up into multiple lines, but this should get you an idea.

Bonus: you can abstract this code even more, if you don't know the contents of $mode exactly:

$modes = [];
foreach ($mode as $key => $value) {
    $modes += array_fill_keys($value, $key);
}
Marijke Luttekes
  • 1,253
  • 1
  • 12
  • 27
  • Can you generalize to more than just two elements? – Barmar Aug 09 '17 at 11:21
  • 1
    Why the -1? This does exactly what the OP wants. You can always use a foreach to abstract this a bit more by making the `array_fill_keys` second argument equal to `$key`. – Marijke Luttekes Aug 09 '17 at 11:22
  • @K.thomas You are literally printing the wrong variable. You are printing `$mode` instead of `$modes` and if you correct that, you see the output is equal to the OP's. – Marijke Luttekes Aug 09 '17 at 11:23
  • You still have a `foreach` loop in the second version. Can you make that functional as well? – Barmar Aug 09 '17 at 11:30
  • @Marijke Luttekes - your foreach approach is far superior to my original nested foreach loops. However, I am really trying to find a functional programming solution for this problem. – knot22 Aug 09 '17 at 11:33
  • @Barmar You're free to make your own answer. I like the foreach for this use case, it's got little overhead and it's very easy to read. – Marijke Luttekes Aug 09 '17 at 11:34
  • @knot22 Why does it have to be functional programming? – Marijke Luttekes Aug 09 '17 at 11:35
  • @MarijkeLuttekes I did post my own answer. – Barmar Aug 09 '17 at 11:36
  • @Marijke Luttekes This problem is particularly well suited to FP because all values are constants AND I am trying to learn how to implement FP in PHP. – knot22 Aug 09 '17 at 11:37
  • @Barmar Sorry about that, I see it now. – Marijke Luttekes Aug 09 '17 at 11:38
  • @knot22 Well, considering you require both the key and value of the `$mode` array, you cannot use `array_map` as that one only handles values. You can try `array_walk`, but I can't imagine you'll get clean code out of it. I doubt you can make code that's less complicated and has less overhead than mine. You can take a shot at it and find a better solution, or you find a better test case than the one in your original question. – Marijke Luttekes Aug 09 '17 at 11:42
  • @MarijkeLuttekes See my answer for how you can get the keys with `array_map`. – Barmar Aug 09 '17 at 11:48
  • @Barmar I've seen it. A double `array_map` with pointers. Yes it does what the OP wants and in such, it's a good answer to this question and therefore I have upvoted it. However, I would never allow it in production code. You probably wouldn't either, given your comment below it. – Marijke Luttekes Aug 09 '17 at 12:08
  • Some context: I did not realize this was an exercise when I wrote the answer. Hence I went for clean code instead of FP (as that seems wrong in this use-case). – Marijke Luttekes Aug 09 '17 at 12:10
-4

use array_flip for exchange key and val together in array :

https://3v4l.org/DeIsG

but because of you want use multidimensional array you should have a loop :

<?php
$mode[0] = array(1, 2, 5, 6);
$mode[1] = array(3, 4, 7, 8);
for($i=0;$i<2;$i++)
{
 $flipped = array_flip($mode[$i]);
 print_r($flipped);
}

?>

output:

Array
(
[1] => 0
[2] => 1
[5] => 2
[6] => 3
)
Array
(
[3] => 0
[4] => 1
[7] => 2
[8] => 3
)