Skip to content

Function to build associative arrays from an input list and two user functions producing keys and values for the output list #164

Description

@gvlasov

This is a feature request. Something like this:

create_map(
  ['cat', 'bear', 'aardvark'],
  function($element, $index, $collection) {
    return strlen($element);
  }
);
/*
returns [
  'cat'=>3,
  'bear'=>4,
  'aardvark'=>8
];
*/

I'm not sure what that function would be called, I admit that it should have a better name than create_map

Couldn't properly explain what I want in the original post, see a comment below: #164 (comment)

Activity

  1. phanan commented on Jul 4, 2018

    @phanan
    Collaborator

    create_assoc? Anw, what might be a use case for this?

  2. gvlasov commented on Jul 6, 2018

    @gvlasov
    Author

    @phanan Sorry, I messed up explaining what I want.

    Basically what I suggest is an analogue to Yii2's ArrayHelpers::map. It doesn't do what map does in functional programming (i.e. taking a list and returning another list), but rather it takes:

    • an input list
    • a function to produce keys of the output list
    • a function to produce values of the output list

    and creates an associative array (a map) from the keys to values for the same elements of the original array, e.g.:

    ArrayMap::map(
      [
        ['id' => 1, 'name' => 'Bob'],
        ['id' => 2, 'name' => 'Joj'],
        ['id' => 3, 'name' => 'Yoy'], 
      ],
      function($elem, $index) {
        return $elem['id'];
      },
      function($elem, $index) {
        return 'Mister '.$elem['name'];
      }
    );
    /*
    returns
    [
     1 => 'Mister Bob',
     2 => 'Mister Joj',
     3 => 'Mister Yoy'
    ]
    */

    Example: I have a list of books. I need to get a map from book ids to their names to pass to a view to render a <select> element

    In plain PHP:

    $ids2Names = [];
    foreach ($books as $book) {
      $ids2Names[$book->id] = $book->name;
    }

    With ArrayHelper::map from Yii2:

    ArrayHelper::map(
      $books,
      function($book) { return $book->id; },
      function($book) { return $book->name; }
    );

    With create_assoc:

    create_assoc(
      $books,
      function($book) { return $book->id; },
      function($book) { return $book->name; }
    );

    Benefit of this approach in comparison to plain php is that it is more semantical and isolates logic for getting keys and values, doesn't require you to come up with the assotiative array name. We can also add a check that no two entries of the input array produce the same key.

  3. gvlasov commented on Jul 6, 2018

    @gvlasov
    Author

    @phanan Also create_asoc can be useful for print debugging in some cases. But most importantly it can be useful in formatting input data in an MVC view.

  4. lstrojny commented on Jul 6, 2018

    @lstrojny
    Owner

    array_combine(map(…, …), map(…, …)) is not good enough?

  5. gvlasov commented on Jul 6, 2018

    @gvlasov
    Author

    @lstrojny

    array_combine(
      map($books, function($book) {
        return $book->id;
      }),
      map($books, function($book) {
          return $book->name;
      })
    );

    vs

    create_assoc(
      $books,
      function($book) { return $book->id; },
      function($book) { return $book->name; }
    );

    Also if we want to do something like this:

    create_assoc(
      filter(
        $bookRepository->getBooks(),
        function() {
           // maybe some more logic
        }
      ),
    
      function($book) { return $book->id; },
      function($book) { return $book->name; }
    );

    then in the array_combine() case we'll have to use an extra variable.

    Also map and the input array are not written twice in create_assoc. Also using create_assoc would explicitly state the relation between keys and values, unlike array_combine where you'd have to parse it visually. I think functional-php has many functions that are much more niche than this one, and ability to create associative arrays easily to me seems to be an essential but missing piece of this library ($preserveKeys everywhere).

  6. phanan commented on Jul 6, 2018

    @phanan
    Collaborator

    Personally, I don't see a big gain :/ Btw, your create_assoc's example should have been written this way for a fairer comparison:

    create_assoc(
      $books,
      function($book) { 
        return $book->id; 
      },
      function($book) {
        return $book->name; 
      }
    );

    As you can see, create_assoc is one LoC longer ;)

  7. gvlasov commented on Jul 6, 2018

    @gvlasov
    Author

    @phanan It is not about LoC, it is about the amount of nested expressions ;;;)))

    Btw, if you rewrite my create_assoc example, then consider doing the same for array_combine:

    array_combine(
      map(
        $books,
        function($book) {
          return $book->id;
        }
      ),
      map(
        $books,
        function($book) {
          return $book->name;
        }
      )
    );
  8. gvlasov commented on Jul 6, 2018

    @gvlasov
    Author

    Ok, let's wait several months till the few people who haven't already searched for a less verbose way to build associative arrays in PHP start upvoting this coming via Google, if you don't think this suggestion is useful.

  9. changed the title [-]Function to build associative arrays from a list of keys and a function to produce its values[/-] [+]Function to build associative arrays from an input list and two user functions producing keys and values for the output list[/+] on Jul 6, 2018
  10. lstrojny commented on Jul 9, 2018

    @lstrojny
    Owner

    Another option that came to mind was to use reindex:

    map(
        function ($book) { return $book->name; },
        reindex(
            function($book) { return $book->id; },
            filter($collection, …)
        )
    )
    

    But I like that the create_assoc reads so well. Let me ponder this a bit longer

  11. tzkmx commented on Nov 21, 2019

    @tzkmx

    What do you think about unfold?
    https://github.com/apantle/fun-php/blob/master/README.md#unfold

    It does already what you are looking for, if the passed function is unary is called with just the input as argument, and allows optional values or even a helper to pass around values between applied functions.

    We use it to apply a bunch of different service calls to a single entry, like a tap but calling many functions to build a single assoc array.

    Happy to merge this little function of mine with more tests and a better suited name to the project.

  12. tzkmx commented on Nov 25, 2019

    @tzkmx

    So @gvlasov, my proposal it very similar to what you have requested, but instead of directly building the associative array, instead I'm returning a function that allow to build any number of associative arrays, based on the same set of rules with a single or a series of input values.

    If there are some issues on my proposal, please let me know to make it more useful. My tests are copied from the independent project I've worked this function, and they are thought more closely to a complement of another library that I use to map associative arrays, reducing them being a common case. This function makes the opposite, that's why in the little collection I've built I called it originally unfold.

  13. drupol commented on May 8, 2021

    @drupol

    Hi,

    Is this what you're looking for?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions