# NAME autobox::Transform - Autobox methods to transform Arrays and Hashes # NOTE This module supercedes autobox::Array::Transform which was unfortunately named. The old module is deprecated and will soon disappear. # SYNOPSIS # Comparison of vanilla Perl and autobox version ### map_by - method call: $books are Book objects 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; ### map_by - hash key: $books are book hashrefs my @genres = map { $_->{genre} } @$books; my @genres = $books->map_by("genre"); ### grep_by - method call: $books are Book objects 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]); ### grep_by - hash key: $books are book hashrefs my $sold_out_books = [ grep { $_->{is_sold_out} } @$books ]; my $sold_out_books = $books->grep_by("is_sold_out"); ### group_by - method call: $books are Book objects $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 ], # }, ### group_by - hash key: $books are book hashrefs $books->group_by("title"), # $books are hashrefs #### flat - $author->books returns an arrayref of Books my $author_books = [ map { @{$_->books} } @$authors ] my $author_books = $authors->map_by("books")->flat # DESCRIPTION High level autobox methods you can call on arrays, arrayrefs, hashes and hashrefs e.g. - $array->map\_by() - $array->grep\_by() - $array->group\_by() - $array->flat() ## 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(", "); ## Transforming lists of objects vs list of hashrefs map\_by, grep\_by etc are called the same way regardless of whether the list contains objects or hashrefs. The items in the list must all be either objects or hashrefs. If the array contains objects, a method is called on each object (possibly with the arguments provided). If the array contains hashrefs, the hash key is looked up on each item. ## 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. Beware: you might be in list context when you need an arrayref. 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 ## @array->map\_by($accessor, @$args?) : @array | @$array Call the $accessor on each object in @array, or get the hash key value on each hashref in the list. Like: map { $_->$accessor() } # or map { $_->{$accessor} } 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 { $_->$accessor(@$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 ]); Or get the hash key value. Examples: my @review_scores = $reviews->map_by("score"); ## @array->grep\_by($accessor, @$args?) : @array | @$array Call the $accessor on each object in the list, or get the hash key value on each hashref in the list. Like: grep { $_->$accessor() } grep { $_->{$accessor} } Examples: my @prolific_authors = $authors->grep_by("is_prolific"); Optionally pass in @$args in the method call. Like: grep { $_->$accessor(@$args) } Examples: my @books_to_charge_for = $books->grep_by("price_with_tax", [ $tax_pct ]); ## @array->group\_by($accessor, @$args = \[\], $value\_sub = object) : %key\_value | %$key\_value Call ->$accessor(@$args) on each object in the array, or get the hash key for each hashref 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->$accessor(@$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->$accessor(@$args) ## @array->group\_by\_count($accessor, @$args = \[\]) : %key\_count | %$key\_count Just like group\_by, but the hash values are the the number of instances each $accessor 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. ## @array->group\_by\_array($accessor, @$args = \[\]) : %key\_objects | %$key\_objects Just like group\_by, but the hash values are arrayrefs containing the objects which has each $accessor 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. ## @array->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. # AUTOBOX HASH METHODS # 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.