Map, Filter, and Reduce in PHP

D12eaf3ef46e4f0fc6b714fd2b7ffe3b?s=47 derek-b
January 25, 2020

Map, Filter, and Reduce in PHP

Even though Map Reduce was created for parallelization, which PHP does not natively support, the syntax is supported. In this talk I will outline how to use map, filter, and reduce with PHP collections and the benefits you can gain over traditional for loops.

In this talk we will explore different methods of processing and manipulating collections of data giving you new tools in your developer’s toolbox

D12eaf3ef46e4f0fc6b714fd2b7ffe3b?s=128

derek-b

January 25, 2020
Tweet

Transcript

  1. 2.

    @DerekB_WI About Me • Father of 3, Husband • Software

    Consultant with Spark Labs • PHP, Java, Groovy, JavaScript … • He/Him/His
  2. 3.
  3. 6.

    @DerekB_WI Google in 2004 • MapReduce: Simplified Data Processing on

    Large Clusters, Jeffrey Dean and Sanjay Ghemawat
  4. 7.
  5. 8.
  6. 10.

    @DerekB_WI Java Data Streams List<Integer> numbers = Arrays.asList(5, 22, 457,

    111233, 6); List<Integer> filteredNumbers = (List<Integer>) numbers.stream() .filter(num -> (int)num > 10) .sorted() .map(num -> (int)num * -1) .collect(Collectors.toList());
  7. 11.

    @DerekB_WI Java Data Streams List<Integer> numbers = Arrays.asList(5, 22, 457,

    111233, 6); Integer sum = numbers.stream() .reduce(0, (subtotal, element) -> subtotal + element);
  8. 12.

    @DerekB_WI Java Data Streams List<Integer> numbers = Arrays.asList(5, 22, 457,

    111233, 6); List<Integer> filteredNumbers = (List<Integer>) numbers.parallelStream() .filter(num -> (int)num > 10) .sorted() .map(num -> (int)num * -1) .collect(Collectors.toList()); Integer sum = numbers.parallelStream() .reduce(0, (subtotal, element) -> subtotal + element);
  9. 13.
  10. 14.

    @DerekB_WI JavaScript Promise function loadImage() { let imageUrl = 'https://live.staticflickr.com/

    65535/47813616341_6063c7b420_b_d.jpg'; fetch(imageUrl) .then((data) => { // Save image and display ... }) .catch((error) => { // Alert user of error ... }); }
  11. 15.
  12. 16.

    @DerekB_WI Benefits • No Parallel Processing, So what else? •

    Functional, no side-effects • Easy to read • Single Responsibility • Reduce boilerplate
  13. 17.
  14. 18.

    @DerekB_WI Traditional Foreach $placesArray = $reader->getFileContents();
 
 $filteredLocations = [];

    foreach ($placesArray as $place) {
 if ($place['state'] == 'AK') {
 $filteredLocations[] = new Place($place);
 }
 }
  15. 22.
  16. 23.

    @DerekB_WI Traditional Foreach $placesArray = $reader->getFileContents();
 
 $filteredLocations = [];

    foreach ($placesArray as $place) {
 if ($place['state'] == 'AK') {
 $filteredLocations[] = new Place($place);
 }
 }
  17. 25.
  18. 26.
  19. 27.

    @DerekB_WI Reduce $myTrips = [
 new Trip("name" => "school", "distance"

    => 1.5),
 new Trip("name" => "the store", "distance" => 2.5)]; $totalDriven = array_reduce($myTrips,
 function ($total, $trip) {
 return $total + $trip->distance;
 },
 0 // initial
 ); echo "I biked {$totalDriven} kilometers today.";
  20. 28.

    @DerekB_WI Reduce $myTrips = [
 new Trip("name" => "school", "distance"

    => 1.5),
 new Trip("name" => "the store", "distance" => 2.5)]; $totalDriven = array_reduce($myTrips,
 function ($total, $trip) {
 return $total + $trip->distance;
 },
 0 // initial
 ); echo "I biked {$totalDriven} kilometers today.";
  21. 30.

    @DerekB_WI Traditional Loop for ($i = 0; $i < count($placesArray);

    $i++) {
 $stateName = $placesArray[$i]['state']; if (!array_key_exists($stateName, $stateCounters)) 
 {
 $stateCounters[$stateName] = 0;
 } $stateCounters[$stateName]++; }
  22. 31.

    @DerekB_WI Reduce $stateCounters = array_reduce($placesArray,
 function($totals, $location) {
 $totals[$location['state']] =


    isset($totals[$location['state']])
 ? $totals[$location['state']] + 1 : 1;
 return $totals;
 });
  23. 32.

    @DerekB_WI Reduce $stateCounters = array_reduce($placesArray,
 function($totals, $location) {
 $totals[$location['state']] =


    isset($totals[$location['state']])
 ? $totals[$location['state']] + 1 : 1;
 return $totals;
 });
  24. 33.

    @DerekB_WI Reduce $stateCounters = array_reduce($placesArray,
 function($totals, $location) {
 $totals[$location['state']] =


    isset($totals[$location['state']])
 ? $totals[$location['state']] + 1 : 1;
 return $totals;
 });
  25. 34.

    @DerekB_WI Use an outside variable $searchWord = 'Bridge'; $placesWithBridges =

    array_filter($placesArray,
 function($location) use($searchWord) {
 return strpos($location['name'], $searchWord);
 });
  26. 36.

    @DerekB_WI Patterns /**
 * route - /api/place
 */ public function

    allPlaces() {
 $places = $this->repository->retrieveAll(); 
 $placesDtos = array_map(
 function($place) {
 return PlacesTransformer::toDTO($place);
 }, $places); return convertToJson($placesDtos); }
  27. 37.

    @DerekB_WI Patterns /**
 * route - /api/user/$userId/place
 */ public function

    placesByUser($userId) {
 $places = $this->repository->retrieveAll(); 
 $placesDtos = array_map(
 function($place) {
 return PlacesTransformer::toDTO($place);
 }, array_filter($places, function($place) {
 return LegacyMiddleware::shouldInclude($place);
 }
 )); return convertToJson($placesDtos);
 }
  28. 38.
  29. 39.

    @DerekB_WI Immutable $searchWord = 'Bridge'; $placesWithBridges = array_filter($placesArray,
 function($location) use($searchWord)

    { $searchWord = ‘something else’;
 return strpos($location['name'], $searchWord);
 });
  30. 40.

    @DerekB_WI Mutable array_walk(
 $placesArray,
 function(&$location, $key) {
 $location['distance'] = calcDistance($location);


    }); array_multisort(
 array_column($placesArray, 'distance'),
 SORT_ASC,
 $placesArray);
  31. 43.

    @DerekB_WI Generators • Pro: Reduced Memory Usage • Pro: Easy

    syntax • Con: Cannot use array functions
  32. 44.

    @DerekB_WI Generator Map function map(Traversable $data, Callable $func) { foreach($data

    as $item) {
 yield call_user_func($func, $item);
 }
 }
  33. 46.

    @DerekB_WI Generator Filter function filter(Traversable $data, Callable $func) { foreach($data

    as $item) {
 if (call_user_func($func)) {
 yield $item;
 }
 }
 }
  34. 48.

    @DerekB_WI Data Structures • Standard PHP Library - SPL •

    Common Structures - widely used in other languages • Many have filter and reduce functions • apt install php-ds • pecl install ds
  35. 49.

    @DerekB_WI Hash Map $map = new DS\Map();
 $map->put('AK', PlaceFactory::make('Alaska'));
 $map->put('WI',

    PlaceFactory::make('Wisconsin')); $map->filter(function($key, $value) {
 return $value->population > 5000000;
 });
  36. 50.

    @DerekB_WI Hash Map $map = new DS\Map();
 $map->put('AK', PlaceFactory::make('Alaska'));
 $map->put('WI',

    PlaceFactory::make('Wisconsin')); $map->reduce(function($carry, $key, $value) {
 return $carry + $value->population;
 }, 0);
  37. 51.
  38. 55.

    @DerekB_WI Iterators public function next() {
 $this->index++; if ($this->index >=

    count($this->data)) {
 $this->valid = false;
 return null;
 } return $this->current(); }
  39. 56.

    @DerekB_WI Iterators public function key() {
 return $this->index;
 } public

    function valid() {
 return $this->valid;
 } public function rewind() {
 $this->index = 0;
 }
  40. 66.

    @DerekB_WI Promise use GuzzleHttp\Promise\Promise; $promise = new Promise(function() { …

    }); $result = $promise->then(
 function($value) {
 // handle result
 }
 );
  41. 68.
  42. 69.