# NAME autobox::Transform - Autobox methods to transform Arrays and Hashes # SYNOPSIS # These are equivalent ways to transform arrays and arrayrefs ### map_by my @genres = map { $_->genre() } @$books; my @genres = $books->map_by("genre"); my $genres = [ map { $_->genre() } @$books ]; my $genres = $books->map_by("genre"); # With sum from autobox::Core / List::AllUtils my $book_order_total = sum( map { $_->price_with_tax($tax_pct) } @{$order->books} ); my $book_order_total = $order->books ->map_by(price_with_tax => [$tax_pct])->sum; ### grep_by my $sold_out_books = [ grep { $_->is_sold_out } @$books ]; my $sold_out_books = $books->grep_by("is_sold_out"); my $books_in_library = [ grep { $_->is_in_library($library) } @$books ]; my $books_in_library = $books->grep_by(is_in_library => [$library]); ### group_by $books->group_by("title"), # { # "Leviathan Wakes" => $books->[0], # "Caliban's War" => $books->[1], # "The Tree-Body Problem" => $books->[2], # "The Name of the Wind" => $books->[3], # }, $authors->group_by(publisher_affiliation => ["with"]), # { # 'James A. Corey with Orbit' => $authors->[0], # 'Cixin Liu with Head of Zeus' => $authors->[1], # 'Patrick Rothfuss with Gollanz' => $authors->[2], # }, $books->group_by_count("genre"), # { # "Sci-fi" => 3, # "Fantasy" => 1, # }, my $genre_books = $books->group_by_array("genre"); # { # "Sci-fi" => [ $sf_book_1, $sf_book_2, $sf_book_3 ], # "Fantasy" => [ $fantasy_book_1 ], # }, #### flat my $prolific_author_books = [ map { @{$_->books} } @$authors ] my $prolific_author_books = $authors->map_by("books")->flat # DESCRIPTION Note: This module supercedes autobox::Array::Transform which was unfortunately named. High level autobox methods you can call on arrays, arrayrefs, hashes and hashrefs e.g. map\_by(), grep\_by(), group\_by() ## Raison d'etre [autobox::Core](https://metacpan.org/pod/autobox::Core) is awesome, for a variety of reasons. - It cuts down on dereferencing punctuation clutter. - It makes map and grep transforms read in the same direction it's executed. - It makes it easier to write those things in a natural order. No need to move the cursor around a lot just to fix dereferencing, order of operations etc. autobox::Transform provides a few higher level methods for mapping, greping and sorting common cases which are easier to read and write. Since they are at a slightly higher semantic level, once you know them they also provide a more specific meaning than just "map" or "grep". (Compare the difference between seeing a "map" and seeing a "foreach" loop. Just seeing the word "map" hints at what type of thing is going on here: transforming a list into another list). The methods of autobox::Transform are not suitable for all cases, but when used appropriately they will lead to much more clear, succinct and direct code, especially in conjunction with autobox::Core. ## Examples my $total_order_amount = $order->books ->map_by(price_with_tax => [ $tax_pct ]) ->sum; my $order_authors = $order->books ->map_by("author") ->map_by("name")->uniq->sort->join(", "); ## List and Scalar Context All of the methods below are context sensitive, i.e. they return a list in list context and an arrayref in scalar context, just like autobox::Core. When in doubt, assume they work like `map` and `grep`, and convert the return value to references where you might have an unobvious list context. E.g. $self->my_method( # Wrong, this is list context and wouldn't return an arrayref books => $books->grep_by("is_published"), ), $self->my_method( # Correct, convert the list to an arrayref books => [ $books->grep_by("is_published") ], ), $self->my_method( # Correct, ensure scalar context i.e. an array ref books => scalar $books->grep_by("is_published"), ), # AUTOBOX ARRAY METHODS ## map\_by($method, @$args?) : @array | @$array Call the $method on each item in the list. Like: map { $_->$method() } Examples: my @ahthor_names = $authors->map_by("name"); my $author_names = @publishers->map_by("authors")->map_by("name"); Optionally pass in @$args in the method call. Like: map { $_->$method(@$args) } Examples: my @prices_including_tax = $books->map_by("price_with_tax", [ $tax_pct ]); my $prices_including_tax = $books->map_by(price_with_tax => [ $tax_pct ]); ## grep\_by($method, @$args?) : @array | @$array Call the $method on each item in the list. Like: grep { $_->$method() } Examples: my @prolific_authors = $authors->grep_by("is_prolific"); Optionally pass in @$args in the method call. Like: grep { $_->$method(@$args) } Examples: my @books_to_charge_for = $books->grep_by("price_with_tax", [ $tax_pct ]); ## group\_by($method, @$args = \[\], $value\_sub = object) : %key\_value | %$key\_value Call ->$method(@$args) on each object in the array (just like ->map\_by) and group the return values as keys in a hashref. The default $value\_sub puts the objects in the list as the hash values. Example: my $title_book = $books->group_by("title"); # { # "Leviathan Wakes" => $books->[0], # "Caliban's War" => $books->[1], # "The Tree-Body Problem" => $books->[2], # "The Name of the Wind" => $books->[3], # }, ### The $value\_sub This is a bit tricky to use, so the most common thing would probably be to use one of the more specific group\_by-methods which do common things (see below). It should be capable enough to achieve what you need though, so here's how it works: The hash key is whatever is returned from $object->$method(@$args). The hash value is whatever is returned from my $new_value = $value_sub->($current_value, $object, $key); where: - $current value is the current hash value for this key (or undef if the first one). - $object is the current item in the list. The current $\_ is also set to this. - $key is the key returned by $object->$method(@$args) ## group\_by\_count($method, @$args = \[\]) : %key\_count | %$key\_count Just like group\_by, but the hash values are the the number of instances each $method value occurs in the list. Example: $books->group_by_count("genre"), # { # "Sci-fi" => 3, # "Fantasy" => 1, # }, $book->genre() returns the genre string. There are three books counted for the "Sci-fi" key. ## group\_by\_array($method, @$args = \[\]) : %key\_objects | %$key\_objects Just like group\_by, but the hash values are arrayrefs containing the objects which has each $method value. Example: my $genre_books = $books->group_by_array("genre"); # { # "Sci-fi" => [ $sf_book_1, $sf_book_2, $sf_book_3 ], # "Fantasy" => [ $fantasy_book_1 ], # }, $book->genre() returns the genre string. The three Sci-fi book objects are collected under the Sci-fi key. ## flat() : @array | @$array Return a flattened array, assuming the array items themselves are array refs. I.e. [ [ 1, 2, 3 ], [ "a", "b" ], ]->flat returns [ 1, 2, 3, "a", "b "] This is useful if e.g. a map\_by("some\_method") returns arrayrefs of objects which you want to do further method calls on. Example: # ->books returns an arrayref of Book objects with a ->title $authors->map_by("books")->flat->map_by("title") Note: This is different from autobox::Core's ->flatten, which reurns a list rather than an array and therefore can't be used in this way. # DEVELOPMENT ## Author Johan Lindstrom, `` ## Source code [https://github.com/jplindstrom/p5-autobox-Transform](https://github.com/jplindstrom/p5-autobox-Transform) ## Bug reports Please report any bugs or feature requests on GitHub: [https://github.com/jplindstrom/p5-autobox-Transform/issues](https://github.com/jplindstrom/p5-autobox-Transform/issues). # COPYRIGHT & LICENSE Copyright 2016- Johan Lindstrom, All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.