.\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "autobox::Transform 3pm" .TH autobox::Transform 3pm "2020-07-30" "perl v5.30.3" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" autobox::Transform \- Autobox methods to transform Arrays and Hashes .SH "CONTEXT" .IX Header "CONTEXT" autobox provides the ability to call methods on native types, e.g. strings, arrays, and hashes as if they were objects. .PP autobox::Core provides the basic methods for Perl core functions like \f(CW\*(C`uc\*(C'\fR, \f(CW\*(C`map\*(C'\fR, and \f(CW\*(C`grep\*(C'\fR. .PP This module, \f(CW\*(C`autobox::Transform\*(C'\fR, provides higher level and more specific methods to transform and manipulate arrays and hashes, in particular when the values are hashrefs or objects. .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 2 \& use autobox::Core; # map, uniq, sort, join, sum, etc. \& use autobox::Transform; .Ve .SS "Arrays" .IX Subsection "Arrays" .Vb 1 \& # use autobox::Core for \->map etc. \& \& # filter (like a more versatile grep) \& $book_locations\->filter(); # true values \& $books\->filter(sub { $_\->is_in_library($library) }); \& $book_names\->filter( qr/lord/i ); \& $book_genres\->filter("scifi"); \& $book_genres\->filter({ fantasy => 1, scifi => 1 }); # hash key exists \& \& # reject: the inverse of filter \& $book_genres\->reject("fantasy"); \& \& # order (like a more succinct sort) \& $book_genres\->order; \& $book_genres\->order("desc"); \& $book_prices\->order([ "num", "desc" ]); \& $books\->order([ sub { $_\->{price} }, "desc", "num" ]); \& $log_lines\->order([ num => qr/pid: "(\ed+)"/ ]); \& $books\->order( \& [ sub { $_\->{price} }, "desc", "num" ] # first price \& sub { $_\->{name} }, # then name \& ); \& \& # group (aggregate) array into hash \& $book_genres\->group; # "Sci\-fi" => "Sci\-fi" \& $book_genres\->group_count; # "Sci\-fi" => 3 \& $book_genres\->group_array; # "Sci\-fi" => [ "Sci\-fi", "Sci\-fi", "Sci\-fi"] \& \& # Flatten arrayrefs\-of\-arrayrefs \& $authors\->map_by("books") # \->books returns an arrayref \& # [ [ $book1, $book2 ], [ $book3 ] ] \& $authors\->map_by("books")\->flat; \& # [ $book1, $book2, $book3 ] \& \& # Return reference, even in list context, e.g. in a parameter list \& $book_locations\->filter()\->to_ref; \& \& # Return array, even in scalar context \& @books\->to_array; \& \& # Turn paired items into a hash \& @titles_books\->to_hash; .Ve .SS "Arrays where the items are hashrefs/objects" .IX Subsection "Arrays where the items are hashrefs/objects" .Vb 4 \& # $books and $authors below are arrayrefs with either objects or \& # hashrefs (the call syntax is the same). These have methods/hash \& # keys like C<$book\->genre()>, C<$book\->{is_in_stock}>, \& # C<$book\->is_in_library($library)>, etc. \& \& $books\->map_by("genre"); \& $books\->map_by([ price_with_tax => $tax_pct ]); \& \& $books\->filter_by("is_in_stock"); \& $books\->filter_by([ is_in_library => $library ]); \& $books\->filter_by([ price_with_tax => $rate ], sub { $_ > 56.00 }); \& $books\->filter_by("price", sub { $_ > 56.00 }); \& $books\->filter_by("author", "James A. Corey"); \& $books\->filter_by("author", qr/corey/i); \& \& # grep_by is an alias for filter_by \& $books\->grep_by("is_in_stock"); \& \& # reject_by: the inverse of filter_by \& $books\->reject_by("is_sold_out"); \& \& $books\->uniq_by("id"); \& \& $books\->order_by("name"); \& $books\->order_by(name => "desc"); \& $books\->order_by(price => "num"); \& $books\->order_by(price => [ "num", "desc" ]); \& $books\->order_by(name => [ sub { uc($_) }, "desc" ]); \& $books\->order_by([ price_with_tax => $rate ] => "num"); \& $books\->order_by( \& author => "str", # first by author \& price => [ "num", "desc" ], # then by price, most expensive first \& ); \& $books\->order_by( \& author => [ "desc", sub { uc($_) } ], \& [ price_with_tax => $rate ] => [ "num", "desc" ], \& "name", \& ); \& \& \& $books\->group_by("title"), \& # { \& # "Leviathan Wakes" => $books\->[0], \& # "Caliban\*(Aqs War" => $books\->[1], \& # "The Tree\-Body Problem" => $books\->[2], \& # "The Name of the Wind" => $books\->[3], \& # }, \& \& $authors\->group_by([ publisher_affiliation => "with" ]), \& # { \& # \*(AqJames A. Corey with Orbit\*(Aq => $authors\->[0], \& # \*(AqCixin Liu with Head of Zeus\*(Aq => $authors\->[1], \& # \*(AqPatrick Rothfuss with Gollanz\*(Aq => $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 ], \& # }, .Ve .SS "Hashes" .IX Subsection "Hashes" .Vb 8 \& # map over each pair \& # e.g. Upper\-case the genre name, and make the count say "n books" \& # (return a key => value pair) \& $genre_count\->map_each(sub { uc( $_[0] ) => "$_ books" }); \& # { \& # "FANTASY" => "1 books", \& # "SCI\-FI" => "3 books", \& # }, \& \& # map over each value \& # e.g. Make the count say "n books" \& # (return the new value) \& $genre_count\->map_each_value(sub { "$_ books" }); \& # { \& # "Fantasy" => "1 books", \& # "Sci\-fi" => "3 books", \& # }, \& \& # map each pair into an array \& # e.g. Transform each pair to the string "n: genre" \& # (return list of items) \& $genre_count\->map_each_to_array(sub { "$_: $_[0]" }); \& # [ "1: Fantasy", "3: Sci\-fi" ] \& \& # filter each pair \& # Genres with more than five books \& $genre_count\->filter_each(sub { $_ > 5 }); \& \& # filter out each pair \& # Genres with more than five books \& $genre_count\->reject_each(sub { $_ <= 5 }); \& \& \& # Return reference, even in list context, e.g. in a parameter list \& %genre_count\->to_ref; \& \& # Return hash, even in scalar context \& $author\->book_count\->to_hash; \& \& # Turn key\-value pairs into an array \& %isbn_\|_book\->to_array; .Ve .SS "Combined examples" .IX Subsection "Combined examples" .Vb 7 \& my $order_authors = $order\->books \& \->filter_by("title", qr/^The/) \& \->uniq_by("isbn") \& \->map_by("author") \& \->uniq_by("name") \& \->order_by(publisher => "str", name => "str") \& \->map_by("name")\->uniq\->join(", "); \& \& my $total_order_amount = $order\->books \& \->reject_by("is_sold_out") \& \->filter_by([ covered_by_vouchers => $vouchers ], sub { ! $_ }) \& \->map_by([ price_with_tax => $tax_pct ]) \& \->sum; .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\f(CW\*(C`autobox::Transform\*(C'\fR provides high level autobox methods you can call on arrays, arrayrefs, hashes and hashrefs. .SS "Transforming lists of objects vs list of hashrefs" .IX Subsection "Transforming lists of objects vs list of hashrefs" \&\f(CW\*(C`map_by\*(C'\fR, \f(CW\*(C`filter_by\*(C'\fR \f(CW\*(C`order_by\*(C'\fR etc. (all methods named \f(CW*_by\fR) work with sets of hashrefs or objects. .PP These methods are called the same way regardless of whether the array contains objects or hashrefs. The items in the list must be either all objects or all hashrefs. .PP If the array contains hashrefs, the hash key is looked up on each item. .PP If the array contains objects, a method is called on each object (possibly with the arguments provided). .PP \fICalling accessor methods with arguments\fR .IX Subsection "Calling accessor methods with arguments" .PP For method calls, it's possible to provide arguments to the method. .PP Consider \f(CW\*(C`map_by\*(C'\fR: .PP .Vb 1 \& $array\->map_by($accessor) .Ve .PP If the \f(CW$accessor\fR is a string, it's a simple method call. .PP .Vb 3 \& # method call without args \& $books\->map_by("price") \& # becomes $_\->price() or $_\->{price} .Ve .PP If the \f(CW$accessor\fR is an arrayref, the first item is the method name, and the rest of the items are the arguments to the method. .PP .Vb 3 \& # method call with args \& $books\->map_by([ price_with_discount => 5.0 ]) \& # becomes $_\->price_with_discount(5.0) .Ve .SS "Filter predicates" .IX Subsection "Filter predicates" There are several methods that filter items, e.g. \f(CW\*(C`@array\->filter\*(C'\fR (duh), \f(CW\*(C`@array\->filter_by\*(C'\fR, and \&\f(CW\*(C`%hash\->filter_each\*(C'\fR. These methods take a \f(CW$predicate\fR argument to determine which items to retain or filter out. .PP The \f(CW\*(C`reject\*(C'\fR family of methods do the opposite, and \fIfilter out\fR items that match the predicate, i.e. the opposite of the filter methods. .PP If \f(CW$predicate\fR is an \fIunblessed scalar\fR, it is compared to each value with \f(CW\*(C`string eq\*(C'\fR. .PP .Vb 1 \& $books\->filter_by("author", "James A. Corey"); .Ve .PP If \f(CW$predicate\fR is a \fIregex\fR, it is compared to each value with \f(CW\*(C`=~\*(C'\fR. .PP .Vb 1 \& $books\->reject_by("author", qr/Corey/); .Ve .PP If \f(CW$predicate\fR is a \fIhashref\fR, values in \f(CW@array\fR are retained if the \&\f(CW$predicate\fR hash key \f(CW\*(C`exists\*(C'\fR (the hash values are irrelevant). .PP .Vb 7 \& $books\->filter_by( \& "author", { \& "James A. Corey" => undef, \& "Cixin Liu" => 0, \& "Patrick Rothfuss" => 1, \& }, \& ); .Ve .PP If \f(CW$predicate\fR is a \fIsubref\fR, the subref is called for each value to check whether this item should remain in the list. .PP The \f(CW$predicate\fR subref should return a true value to remain. \f(CW$_\fR is set to the current \f(CW$value\fR. .PP .Vb 1 \& $authors\->filter_by(publisher => sub { $_\->name =~ /Orbit/ }); .Ve .SS "Sorting using order and order_by" .IX Subsection "Sorting using order and order_by" Let's first compare how sorting is done with Perl's \f(CW\*(C`sort\*(C'\fR and autobox::Transform's \f(CW\*(C`order\*(C'\fR/\f(CW\*(C`order_by\*(C'\fR. .PP \fISorting with sort\fR .IX Subsection "Sorting with sort" .IP "\(bu" 4 provide a sub that returns the comparison outcome of two values: \f(CW$a\fR and \f(CW$b\fR .IP "\(bu" 4 in case of a tie, provide another comparison of \f(CW$a\fR and \f(CW$b\fR .PP .Vb 6 \& # If the name is the same, compare age (oldest first) \& sort { \& uc( $a\->{name} ) cmp uc( $b\->{name} ) # first comparison \& || \& int( $b\->{age} / 10 ) <=> int( $a\->{age} / 10 ) # second comparison \& } @users .Ve .PP (note the opposite order of \f(CW$a\fR and \f(CW$b\fR for the age comparison, something that's often difficult to discern at a glance) .PP \fISorting with order, order_by\fR .IX Subsection "Sorting with order, order_by" .IP "\(bu" 4 Provide order options for how one value should be compared with the others: .RS 4 .IP "\(bu" 8 how to compare (\f(CW\*(C`cmp\*(C'\fR or \f(CW\*(C`<=>\*(C'\fR) .IP "\(bu" 8 which direction to sort (\f(CW\*(C`asc\*(C'\fRending or \f(CW\*(C`desc\*(C'\fRending) .IP "\(bu" 8 which value to compare, using a regex or subref, e.g. by \f(CW\*(C`uc($_)\*(C'\fR .RE .RS 4 .RE .IP "\(bu" 4 In case of a tie, provide another comparison .PP .Vb 1 \& # If the name is the same, compare age (oldest first) \& \& # \->order \& @users\->order( \& sub { uc( $_\->{name} ) }, # first comparison \& [ "num", sub { int( $_\->{age} / 10 ) }, "desc" ], # second comparison \& ) \& \& # \->order_by \& @users\->order_by( \& name => sub { uc }, # first comparison \& age => [ num => desc => sub { int( $_ / 10 ) } ], # second comparison \& ) .Ve .PP \fIComparison Options\fR .IX Subsection "Comparison Options" .PP If there's only one option for a comparison (e.g. \f(CW\*(C`num\*(C'\fR), provide a single option (string/regex/subref) value. If there are many options, provide them in an arrayref in any order. .PP \fIComparison operator\fR .IX Subsection "Comparison operator" .IP "\(bu" 4 \&\f(CW"str"\fR (cmp) \- default .IP "\(bu" 4 \&\f(CW"num"\fR (<=>) .PP \fISort order\fR .IX Subsection "Sort order" .IP "\(bu" 4 \&\f(CW"asc"\fR (ascending) \- default .IP "\(bu" 4 \&\f(CW"desc"\fR (descending) .PP \fIThe value to compare\fR .IX Subsection "The value to compare" .IP "\(bu" 4 A subref \- default is: \f(CW\*(C`sub { $_ }\*(C'\fR .RS 4 .IP "\(bu" 8 The return value is used in the comparison .RE .RS 4 .RE .IP "\(bu" 4 A regex, e.g. \f(CW\*(C`qr/id: (\ed+)/\*(C'\fR .RS 4 .IP "\(bu" 8 The value of \f(CW\*(C`join("", @captured_groups)\*(C'\fR are used in the comparison (\f(CW@captured_groups\fR are \f(CW$1\fR, \f(CW$2\fR, \f(CW$3\fR etc.) .RE .RS 4 .RE .PP \fIExamples of a single comparison\fR .IX Subsection "Examples of a single comparison" .PP .Vb 9 \& # order: the first arg is the comparison options (one or an \& # arrayref with many options) \& \->order() # Defaults to str, asc, $_, just like sort \& \->order("num") \& \->order(sub { uc($_) }) \& # compare captured matches, e.g. "John" and "Doe" as "JohnDoe" \& \->order( qr/first_name: (\ew+), last_name: (\ew+)/ ) \& \->order([ num => qr/id: (\ed+)/ ]) \& \->order([ sub { int($_) }, "num" ]) \& \& # order_by: the first arg is the accessor, just like with \& # map_by. Second arg is the comparison options (one or an arrayref \& # with many options) \& \->order_by("id") \& \->order_by("id", "num") \& \->order_by("id", [ "num", "desc" ]) \& \->order_by("name", sub { uc($_) }) \& \->order_by(log_line => qr/first_name: (\ew+), last_name: (\ew+)/ ) \& \->order_by("log_line", [ num => qr/id: (\ed+)/ ]) \& \->order_by(age => [ sub { int($_) }, "num" ]) \& \& # compare int( $a\->age_by_interval(10) ) \& \->order_by([ age_by_interval => 10 ] => [ sub { int($_) }, "num" ]) \& # compare uc( $a\->name_with_title($title) ) \& \->order_by([ name_with_title => $title ], sub { uc($_) }) .Ve .PP \fIExamples of fallback comparisons\fR .IX Subsection "Examples of fallback comparisons" .PP When the first comparison is a tie, the subsequent ones are used. .PP .Vb 10 \& # order: list of comparison options (one or an arrayref with many \& # options, per comparison) \& \->order( \& [ sub { $_\->{price} }, "num" ], # First a numeric comparison of price \& [ sub { $_\->{name} }, "desc" ], # or if same, a reverse comparison of the name \& ) \& \->order( \& [ sub { uc($_) }, "desc" ], \& "str", \& ) \& \->order( \& qr/type: (\ew+)/, \& [ num => desc => qr/duration: (\ed+)/ ] \& [ num => sub { /id: (\ed+)/ } ], \& "str", \& ) \& \& # order_by: pairs of accessor\-comparison options \& \->order_by( \& price => "num", # First a numeric comparison of price \& name => "desc", # or if same, a reverse comparison of the name \& ) \& \->order_by( \& price => [ "num", "desc" ], \& name => "str", \& ) \& # accessor is a method call with arg: $_\->price_with_discount($discount) \& \->order_by( \& [ price_with_discount => $discount ] => [ "num", "desc" ], \& name => [ str => sub { uc($_) } ], \& "id", \& ) .Ve .SS "List and Scalar Context" .IX Subsection "List and Scalar Context" Almost all of the methods are context sensitive, i.e. they return a list in list context and an arrayref in scalar context, just like autobox::Core. .PP \&\fBBeware\fR: \fIyou might be in list context when you need an arrayref.\fR .PP When in doubt, assume they work like \f(CW\*(C`map\*(C'\fR and \f(CW\*(C`grep\*(C'\fR (i.e. return a list), and convert the return value to references where you might have an non-obvious list context. E.g. .PP \fIIncorrect\fR .IX Subsection "Incorrect" .PP .Vb 4 \& $self\->my_method( \& # Wrong, this is list context and wouldn\*(Aqt return an array ref \& books => $books\->filter_by("is_published"), \& ); .Ve .PP \fICorrect\fR .IX Subsection "Correct" .PP .Vb 8 \& $self\->my_method( \& # Correct, put the returned list in an anonymous array ref \& books => [ $books\->filter_by("is_published") ], \& ); \& $self\->my_method( \& # Correct, ensure scalar context to get an array ref \& books => scalar $books\->filter_by("is_published"), \& ); \& \& # Probably the nicest, since \->to_ref goes at the end \& $self\->my_method( \& # Correct, use \->to_ref to ensure an array ref is returned \& books => $books\->filter_by("is_published")\->to_ref, \& ); .Ve .SH "METHODS ON ARRAYS" .IX Header "METHODS ON ARRAYS" .ie n .SS "@array\->filter($predicate = *is_true_subref*) : @array | @$array" .el .SS "\f(CW@array\fP\->filter($predicate = *is_true_subref*) : \f(CW@array\fP | @$array" .IX Subsection "@array->filter($predicate = *is_true_subref*) : @array | @$array" Similar to Perl's \f(CW\*(C`grep\*(C'\fR, return an \f(CW@array\fR with values for which \&\f(CW$predicate\fR yields a true value. .PP \&\f(CW$predicate\fR can be a subref, string, undef, regex, or hashref. See \&\*(L"Filter predicates\*(R". .PP The default (no \f(CW$predicate\fR) is a subref which retains true values in the \f(CW@array\fR. .PP \fIExamples\fR .IX Subsection "Examples" .PP .Vb 5 \& my @apples = $fruit\->filter("apple"); \& my @any_apple = $fruit\->filter( qr/apple/i ); \& my @publishers = $authors\->filter( \& sub { $_\->publisher\->name =~ /Orbit/ }, \& ); .Ve .PP \fIfilter and grep\fR .IX Subsection "filter and grep" .PP autobox::Core's \f(CW\*(C`grep\*(C'\fR method takes a subref, just like this method. \f(CW\*(C`filter\*(C'\fR also supports the other predicate types, like string, regex, etc. .ie n .SS "@array\->reject($predicate = *is_false_subref*) : @array | @$array" .el .SS "\f(CW@array\fP\->reject($predicate = *is_false_subref*) : \f(CW@array\fP | @$array" .IX Subsection "@array->reject($predicate = *is_false_subref*) : @array | @$array" Similar to the Unix command \f(CW\*(C`grep \-v\*(C'\fR, return an \f(CW@array\fR with values for which \f(CW$predicate\fR yields a \fIfalse\fR value. .PP \&\f(CW$predicate\fR can be a subref, string, undef, regex, or hashref. See \&\*(L"Filter predicates\*(R". .PP The default (no \f(CW$predicate\fR) is a subref which \fIfilters out\fR true values in the \f(CW@array\fR. .PP Examples: .PP .Vb 5 \& my @apples = $fruit\->reject("apple"); \& my @no_apples = $fruit\->reject( qr/apple/i ); \& my @publishers = $authors\->reject( \& sub { $_\->publisher\->name =~ /Orbit/ }, \& ); .Ve .ie n .SS "@array\->order(@comparisons = (""str"")) : @array | @$array" .el .SS "\f(CW@array\fP\->order(@comparisons = (``str'')) : \f(CW@array\fP | @$array" .IX Subsection "@array->order(@comparisons = (str)) : @array | @$array" Return \f(CW@array\fR ordered according to the \f(CW@comparisons\fR. The default comparison is the same as the default sort, e.g. a normal string comparison of the \f(CW@array\fR values. .PP If the first item in \f(CW@comparison\fR ends in a tie, the next one is used, etc. .PP Each \fIcomparison\fR consists of a single \fIoption\fR or an \fIarrayref of options\fR, e.g. \f(CW\*(C`str\*(C'\fR/\f(CW\*(C`num\*(C'\fR, \f(CW\*(C`asc\*(C'\fR/\f(CW\*(C`desc\*(C'\fR, or a subref/regex. See \&\*(L"Sorting using order and order_by\*(R" for details about how these work. .PP Examples: .PP .Vb 9 \& @book_genres\->order; \& @book_genres\->order("desc"); \& @book_prices\->order([ "num", "desc" ]); \& @books\->order([ sub { $_\->{price} }, "desc", "num" ]); \& @log_lines\->order([ num => qr/pid: "(\ed+)"/ ]); \& @books\->order( \& [ sub { $_\->{price} }, "desc", "num" ] # first price \& sub { $_\->{name} }, # then name \& ); .Ve .ie n .SS "@array\->group($value_subref = item) : %key_value | %$key_value" .el .SS "\f(CW@array\fP\->group($value_subref = item) : \f(CW%key_value\fP | %$key_value" .IX Subsection "@array->group($value_subref = item) : %key_value | %$key_value" Group the \f(CW@array\fR items into a hashref with the items as keys. .PP The default \f(CW$value_subref\fR puts each item in the list as the hash value. If the key is repeated, the value is overwritten with the last object. .PP Example: .PP .Vb 7 \& my $title_book = $book_titles\->group; \& # { \& # "Leviathan Wakes" => "Leviathan Wakes", \& # "Caliban\*(Aqs War" => "Caliban\*(Aqs War", \& # "The Tree\-Body Problem" => "The Tree\-Body Problem", \& # "The Name of the Wind" => "The Name of the Wind", \& # }, .Ve .PP \fIThe \f(CI$value_subref\fI\fR .IX Subsection "The $value_subref" .PP For simple cases of just grouping a single key to a single value, the \&\f(CW$value_subref\fR is straightforward to use. .PP The hash key is the array item. The hash value is whatever is returned from .PP .Vb 1 \& my $new_value = $value_sub\->($current_value, $object, $key); .Ve .IP "\(bu" 4 \&\f(CW$current\fR value is the current hash value for this key (or undef if the first one). .IP "\(bu" 4 \&\f(CW$object\fR is the current item in the list. The current \f(CW$_\fR is also set to this. .IP "\(bu" 4 \&\f(CW$key\fR is the array item. .PP See also: \f(CW\*(C`\->group_by\*(C'\fR. .ie n .SS "@array\->group_count : %key_count | %$key_count" .el .SS "\f(CW@array\fP\->group_count : \f(CW%key_count\fP | %$key_count" .IX Subsection "@array->group_count : %key_count | %$key_count" Just like \f(CW\*(C`group\*(C'\fR, but the hash values are the the number of instances each item occurs in the list. .PP Example: .PP .Vb 5 \& $book_genres\->group_count; \& # { \& # "Sci\-fi" => 3, \& # "Fantasy" => 1, \& # }, .Ve .PP There are three books counted for the \*(L"Sci-fi\*(R" key. .ie n .SS "@array\->group_array : %key_objects | %$key_objects" .el .SS "\f(CW@array\fP\->group_array : \f(CW%key_objects\fP | %$key_objects" .IX Subsection "@array->group_array : %key_objects | %$key_objects" Just like \f(CW\*(C`group\*(C'\fR, but the hash values are arrayrefs containing those same array items. .PP Example: .PP .Vb 5 \& $book_genres\->group_array; \& # { \& # "Sci\-fi" => [ "Sci\-fi", "Sci\-fi", "Sci\-fi" ], \& # "Fantasy" => [ "Fantasy" ], \& # }, .Ve .PP The three Sci-fi genres are collected under the Sci-fi key. .ie n .SS "@array\->\fBflat()\fP : @array | @$array" .el .SS "\f(CW@array\fP\->\fBflat()\fP : \f(CW@array\fP | @$array" .IX Subsection "@array->flat() : @array | @$array" Return a (one level) flattened array, assuming the array items themselves are array refs. I.e. .PP .Vb 5 \& [ \& [ 1, 2, 3 ], \& [ "a", "b" ], \& [ [ 1, 2 ], { 3 => 4 } ] \& ]\->flat .Ve .PP returns .PP .Vb 1 \& [ 1, 2, 3, "a", "b ", [ 1, 2 ], { 3 => 4 } ] .Ve .PP This is useful if e.g. a \f(CW\*(C`\->map_by("some_method")\*(C'\fR returns arrayrefs of objects which you want to do further method calls on. Example: .PP .Vb 2 \& # \->books returns an arrayref of Book objects with a \->title \& $authors\->map_by("books")\->flat\->map_by("title") .Ve .PP Note: This is different from autobox::Core's \f(CW\*(C`\->flatten\*(C'\fR, which reurns a list rather than an array and therefore can't be used in this way. .ie n .SS "@array\->\fBto_ref()\fP : $arrayref" .el .SS "\f(CW@array\fP\->\fBto_ref()\fP : \f(CW$arrayref\fP" .IX Subsection "@array->to_ref() : $arrayref" Return the reference to the \f(CW@array\fR, regardless of context. .PP Useful for ensuring the last array method return a reference while in scalar context. Typically: .PP .Vb 3 \& do_stuff( \& books => $author\->map_by("books")\->to_ref, \& ); .Ve .PP map_by is called in list context, so without \f(CW\*(C`\->to_ref\*(C'\fR it would have return an array, not an arrayref. .ie n .SS "@array\->\fBto_array()\fP : @array" .el .SS "\f(CW@array\fP\->\fBto_array()\fP : \f(CW@array\fP" .IX Subsection "@array->to_array() : @array" Return the \f(CW@array\fR, regardless of context. This is mostly useful if called on a ArrayRef at the end of a chain of method calls. .ie n .SS "@array\->\fBto_hash()\fP : %hash | %$hash" .el .SS "\f(CW@array\fP\->\fBto_hash()\fP : \f(CW%hash\fP | %$hash" .IX Subsection "@array->to_hash() : %hash | %$hash" Return the item pairs in the \f(CW@array\fR as the key-value pairs of a \&\f(CW%hash\fR (context sensitive). .PP Useful if you need to continue calling \f(CW%hash\fR methods on it. .PP Die if there aren't an even number of items in \f(CW@array\fR. .SH "METHODS ON ARRAYS CONTAINING OBJECTS/HASHES" .IX Header "METHODS ON ARRAYS CONTAINING OBJECTS/HASHES" .ie n .SS "@array\->map_by($accessor) : @array | @$array" .el .SS "\f(CW@array\fP\->map_by($accessor) : \f(CW@array\fP | @$array" .IX Subsection "@array->map_by($accessor) : @array | @$array" \&\f(CW$accessor\fR is either a string, or an arrayref where the first item is a string. .PP Call the \f(CW$accessor\fR on each object in \f(CW@array\fR, or get the hash key value on each hashref in \f(CW@array\fR. Like: .PP .Vb 3 \& map { $_\->$accessor() } @array \& # or \& map { $_\->{$accessor} } @array .Ve .PP Examples: .PP .Vb 2 \& my @author_names = $authors\->map_by("name"); \& my $author_names = @publishers\->map_by("authors")\->flat\->map_by("name"); .Ve .PP Or get the hash key value. Example: .PP .Vb 1 \& my @review_scores = $reviews\->map_by("score"); .Ve .PP Alternatively for when \f(CW@array\fR contains objects, the \f(CW$accessor\fR can be an arrayref. The first item is the method name, and the rest of the items are passed as args in the method call. This obviously won't work when the \f(CW@array\fR contains hashrefs. .PP Examples: .PP .Vb 2 \& my @prices_including_tax = $books\->map_by([ "price_with_tax", $tax_pct ]); \& my $prices_including_tax = $books\->map_by([ price_with_tax => $tax_pct ]); .Ve .ie n .SS "@array\->filter_by($accessor, $predicate = *is_true_subref*) : @array | @$array" .el .SS "\f(CW@array\fP\->filter_by($accessor, \f(CW$predicate\fP = *is_true_subref*) : \f(CW@array\fP | @$array" .IX Subsection "@array->filter_by($accessor, $predicate = *is_true_subref*) : @array | @$array" \&\f(CW$accessor\fR is either a string, or an arrayref where the first item is a string. .PP Call the \f(CW$accessor\fR on each object in the list, or get the hash key value on each hashref in the list. .PP Example: .PP .Vb 1 \& my @prolific_authors = $authors\->filter_by("is_prolific"); .Ve .PP Alternatively the \f(CW$accessor\fR is an arrayref. The first item is the accessor name, and the rest of the items are passed as args the method call. This only works when working with objects, not with hashrefs. .PP Example: .PP .Vb 1 \& my @books_to_charge_for = $books\->filter_by([ price_with_tax => $tax_pct ]); .Ve .PP Use the \f(CW$predicate\fR to determine whether the value should remain. \&\f(CW$predicate\fR can be a subref, string, undef, regex, or hashref. See \&\*(L"Filter predicates\*(R". .PP The default (no \f(CW$predicate\fR) is a subref which retains true values in the result \f(CW@array\fR. .PP Examples: .PP .Vb 5 \& # Custom predicate subref \& my @authors = $authors\->filter_by( \& "publisher", \& sub { $_\->name =~ /Orbit/ }, \& ); \& \& # Call method with args and match a regex \& my @authors = $authors\->filter_by( \& [ publisher_affiliation => "with" ], \& qr/Orbit/ }, \& ); .Ve .PP Note: if you do something complicated with a \f(CW$predicate\fR subref, it might be easier and more readable to simply use \&\f(CW\*(C`$array\-$. .PP \fIAlias\fR .IX Subsection "Alias" .PP \&\f(CW\*(C`grep_by\*(C'\fR is an alias for \f(CW\*(C`filter_by\*(C'\fR. Unlike \f(CW\*(C`grep\*(C'\fR vs \f(CW\*(C`filter\*(C'\fR, this one works exaclty the same way. .ie n .SS "@array\->reject_by($accessor, $predicate = *is_false_subref*) : @array | @$array" .el .SS "\f(CW@array\fP\->reject_by($accessor, \f(CW$predicate\fP = *is_false_subref*) : \f(CW@array\fP | @$array" .IX Subsection "@array->reject_by($accessor, $predicate = *is_false_subref*) : @array | @$array" \&\f(CW\*(C`reject_by\*(C'\fR is the same as \f(CW\*(C`filter_by\*(C'\fR, except it \fIfilters out\fR items that matches the \f(CW$predicate\fR. .PP Example: .PP .Vb 1 \& my @unproductive_authors = $authors\->reject_by("is_prolific"); .Ve .PP The default (no \f(CW$predicate\fR) is a subref which \fIfilters out\fR true values in the result \f(CW@array\fR. .ie n .SS "@array\->uniq_by($accessor) : @array | @$array" .el .SS "\f(CW@array\fP\->uniq_by($accessor) : \f(CW@array\fP | @$array" .IX Subsection "@array->uniq_by($accessor) : @array | @$array" \&\f(CW$accessor\fR is either a string, or an arrayref where the first item is a string. .PP Call the $\f(CW\*(C`accessor\*(C'\fR on each object in the list, or get the hash key value on each hashref in the list. Return list of items which have a unique set of return values. The order is preserved. On duplicates, keep the first occurrence. .PP Examples: .PP .Vb 2 \& # You have gathered multiple Author objects with duplicate ids \& my @authors = $authors\->uniq_by("author_id"); .Ve .PP Alternatively the \f(CW$accessor\fR is an arrayref. The first item is the accessor name, and the rest of the items are passed as args the method call. This only works when working with objects, not with hashrefs. .PP Examples: .PP .Vb 3 \& my @example_book_at_price_point = $books\->uniq_by( \& [ price_with_tax => $tax_pct ], \& ); .Ve .ie n .SS "@array\->order_by(@accessor_comparison_pairs) : @array | @$array" .el .SS "\f(CW@array\fP\->order_by(@accessor_comparison_pairs) : \f(CW@array\fP | @$array" .IX Subsection "@array->order_by(@accessor_comparison_pairs) : @array | @$array" Return \f(CW@array\fR ordered according to the \&\f(CW@accessor_comparison_pairs\fR. .PP The comparison value comes from an initial \&\f(CW\*(C`@array\-\*(C'\fRmap_by($accessor)> for each accessor-comparison pair. It is important that the \f(CW$accessor\fR call returns exactly a single scalar that can be compared with the other values. .PP It then works just like with \f(CW\*(C`\->order\*(C'\fR. .PP .Vb 3 \& $books\->order_by("name"); # default order, i.e. "str" \& $books\->order_by(price => "num"); \& $books\->order_by(price => [ "num", "desc" ]); .Ve .PP As with \f(CW\*(C`map_by\*(C'\fR, if the \f(CW$accessor\fR is used on an object, the method call can include arguments. .PP .Vb 1 \& $books\->order_by([ price_wih_tax => $tax_rate ] => "num"); .Ve .PP Just like with \f(CW\*(C`order\*(C'\fR, the value returned by the accessor can be transformed using a sub, or be matched against a regex. .PP .Vb 1 \& $books\->order_by(price => [ num => sub { int($_) } ]); \& \& # Ignore leading "The" in book titles by optionally matching it \& # with a non\-capturing group and the rest with a capturing group \& # paren \& $books\->order_by( title => qr/^ (?: The \es+ )? (.+) /x ); .Ve .PP If a comparison is missing for the last pair, the default is a normal \&\f(CW\*(C`str\*(C'\fR comparison. .PP .Vb 1 \& $books\->order_by("name"); # default "str" .Ve .PP If the first comparison ends in a tie, the next pair is used, etc. Note that in order to provide accessor-comparison pairs, it's often necessary to provide a default \*(L"str\*(R" comparison just to make it a pair. .PP .Vb 4 \& $books\->order_by( \& author => "str", \& price => [ "num", "desc" ], \& ); .Ve .ie n .SS "@array\->group_by($accessor, $value_subref = object) : %key_value | %$key_value" .el .SS "\f(CW@array\fP\->group_by($accessor, \f(CW$value_subref\fP = object) : \f(CW%key_value\fP | %$key_value" .IX Subsection "@array->group_by($accessor, $value_subref = object) : %key_value | %$key_value" \&\f(CW$accessor\fR is either a string, or an arrayref where the first item is a string. .PP Call \f(CW\*(C`\->$accessor\*(C'\fR on each object in the array, or get the hash key for each hashref in the array (just like \f(CW\*(C`\->map_by\*(C'\fR) and group the values as keys in a hashref. .PP The default \f(CW$value_subref\fR puts each object in the list as the hash value. If the key is repeated, the value is overwritten with the last object. .PP Example: .PP .Vb 7 \& my $title_book = $books\->group_by("title"); \& # { \& # "Leviathan Wakes" => $books\->[0], \& # "Caliban\*(Aqs War" => $books\->[1], \& # "The Tree\-Body Problem" => $books\->[2], \& # "The Name of the Wind" => $books\->[3], \& # }, .Ve .PP \fIThe \f(CI$value_subref\fI\fR .IX Subsection "The $value_subref" .PP For simple cases of just grouping a single key to a single value, the \&\f(CW$value_subref\fR is straightforward to use. .PP The hash key is whatever is returned from \f(CW\*(C`$object\->$accessor\*(C'\fR. .PP The hash value is whatever is returned from .PP .Vb 1 \& my $new_value = $value_sub\->($current_value, $object, $key); .Ve .IP "\(bu" 4 \&\f(CW$current\fR value is the current hash value for this key (or undef if the first one). .IP "\(bu" 4 \&\f(CW$object\fR is the current item in the list. The current \f(CW$_\fR is also set to this. .IP "\(bu" 4 \&\f(CW$key\fR is the key returned by \f(CW$object\fR\->$accessor(@$args) .PP A simple example would be to group by the accessor, but instead of the object used as the value you want to look up an attribute on each object: .PP .Vb 2 \& my $book_id_\|_author = $books\->group_by("id", sub { $_\->author }); \& # keys: book id; values: author .Ve .PP If you want to create an aggregate value the \f(CW$value_subref\fR can be a bit tricky to use, so the most common thing would probably be to use one of the more specific group_by\-methods (see below). It should be capable enough to achieve what you need though. .ie n .SS "@array\->group_by_count($accessor) : %key_count | %$key_count" .el .SS "\f(CW@array\fP\->group_by_count($accessor) : \f(CW%key_count\fP | %$key_count" .IX Subsection "@array->group_by_count($accessor) : %key_count | %$key_count" \&\f(CW$accessor\fR is either a string, or an arrayref where the first item is a string. .PP Just like \f(CW\*(C`group_by\*(C'\fR, but the hash values are the the number of instances each \f(CW$accessor\fR value occurs in the list. .PP Example: .PP .Vb 5 \& $books\->group_by_count("genre"), \& # { \& # "Sci\-fi" => 3, \& # "Fantasy" => 1, \& # }, .Ve .PP \&\f(CW\*(C`$book\->genre()\*(C'\fR returns the genre string. There are three books counted for the \*(L"Sci-fi\*(R" key. .ie n .SS "@array\->group_by_array($accessor) : %key_objects | %$key_objects" .el .SS "\f(CW@array\fP\->group_by_array($accessor) : \f(CW%key_objects\fP | %$key_objects" .IX Subsection "@array->group_by_array($accessor) : %key_objects | %$key_objects" \&\f(CW$accessor\fR is either a string, or an arrayref where the first item is a string. .PP Just like \f(CW\*(C`group_by\*(C'\fR, but the hash values are arrayrefs containing the objects which has each \f(CW$accessor\fR value. .PP Example: .PP .Vb 5 \& my $genre_books = $books\->group_by_array("genre"); \& # { \& # "Sci\-fi" => [ $sf_book_1, $sf_book_2, $sf_book_3 ], \& # "Fantasy" => [ $fantasy_book_1 ], \& # }, .Ve .PP \&\f(CW$book\fR\->\fBgenre()\fR returns the genre string. The three Sci-fi book objects are collected under the Sci-fi key. .SH "METHODS ON HASHES" .IX Header "METHODS ON HASHES" .ie n .SS "%hash\->map_each($key_value_subref) : %new_hash | %$new_hash" .el .SS "\f(CW%hash\fP\->map_each($key_value_subref) : \f(CW%new_hash\fP | %$new_hash" .IX Subsection "%hash->map_each($key_value_subref) : %new_hash | %$new_hash" Map each key-value pair in the hash using the \&\f(CW$key_value_subref\fR. Similar to how to how map transforms a list into another list, map_each transforms a hash into another hash. .PP \&\f(CW\*(C`$key_value_subref\->($key, $value)\*(C'\fR is called for each pair (with \&\f(CW$_\fR set to the value). .PP The subref should return an even-numbered list with zero or more key-value pairs which will make up the \f(CW%new_hash\fR. Typically two items are returned in the list (the key and the value). .PP \fIExample\fR .IX Subsection "Example" .PP .Vb 2 \& { a => 1, b => 2 }\->map_each(sub { "$_[0]$_[0]" => $_ * 2 }); \& # Returns { aa => 2, bb => 4 } .Ve .ie n .SS "%hash\->map_each_value($value_subref) : %new_hash | %$new_hash" .el .SS "\f(CW%hash\fP\->map_each_value($value_subref) : \f(CW%new_hash\fP | %$new_hash" .IX Subsection "%hash->map_each_value($value_subref) : %new_hash | %$new_hash" Map each value in the hash using the \f(CW$value_subref\fR, but keep the keys the same. .PP \&\f(CW\*(C`$value_subref\->($key, $value)\*(C'\fR is called for each pair (with \&\f(CW$_\fR set to the value). .PP The subref should return a single value for each key which will make up the \f(CW%new_hash\fR (with the same keys but with new mapped values). .PP \fIExample\fR .IX Subsection "Example" .PP .Vb 2 \& { a => 1, b => 2 }\->map_each_value(sub { $_ * 2 }); \& # Returns { a => 2, b => 4 } .Ve .ie n .SS "%hash\->map_each_to_array($item_subref) : @new_array | @$new_array" .el .SS "\f(CW%hash\fP\->map_each_to_array($item_subref) : \f(CW@new_array\fP | @$new_array" .IX Subsection "%hash->map_each_to_array($item_subref) : @new_array | @$new_array" Map each key-value pair in the hash into a list using the \&\f(CW$item_subref\fR. .PP \&\f(CW\*(C`$item_subref\->($key, $value)\*(C'\fR is called for each pair (with \&\f(CW$_\fR set to the value) in key order. .PP The subref should return zero or more list items which will make up the \f(CW@new_array\fR. Typically one item is returned. .PP \fIExample\fR .IX Subsection "Example" .PP .Vb 2 \& { a => 1, b => 2 }\->map_each_to_array(sub { "$_[0]\-$_" }); \& # Returns [ "a\-1", "b\-2" ] .Ve .ie n .SS "%hash\->filter_each($predicate = *is_true_subref*) : @hash | @$hash" .el .SS "\f(CW%hash\fP\->filter_each($predicate = *is_true_subref*) : \f(CW@hash\fP | @$hash" .IX Subsection "%hash->filter_each($predicate = *is_true_subref*) : @hash | @$hash" Return a \f(CW%hash\fR with values for which \f(CW$predicate\fR yields a true value. .PP \&\f(CW$predicate\fR can be a subref, string, undef, regex, or hashref. See \&\*(L"Filter predicates\*(R". .PP The default (no \f(CW$predicate\fR) is a subref which retains true values in the \f(CW%hash\fR. .PP If the \f(CW$predicate\fR is a subref, \f(CW\*(C`$predicate\->($key, $value)\*(C'\fR is called for each pair (with \f(CW$_\fR set to the value). .PP The subref should return a true value to retain the key-value pair in the result \f(CW%hash\fR. .PP \fIExamples\fR .IX Subsection "Examples" .PP .Vb 2 \& { a => 1, b => 2 }\->filter_each(sub { $_ == 2 }); \& # Returns { b => 2 } \& \& $book_author\->filter_each(sub { $_\->name =~ /Corey/ }); .Ve .ie n .SS "%hash\->reject_each($predicate = *is_false_subref*) : @hash | @$hash" .el .SS "\f(CW%hash\fP\->reject_each($predicate = *is_false_subref*) : \f(CW@hash\fP | @$hash" .IX Subsection "%hash->reject_each($predicate = *is_false_subref*) : @hash | @$hash" \&\f(CW\*(C`reject_each\*(C'\fR is the same as \f(CW\*(C`filter_each\*(C'\fR, except it \fIfilters out\fR items that matches the \f(CW$predicate\fR. .PP Examples: .PP .Vb 2 \& { a => 1, b => 2 }\->reject_each(sub { $_ == 2 }); \& # Returns { a => 1 } .Ve .PP The default (no \f(CW$predicate\fR) is a subref which \fIfilters out\fR true values in the \f(CW%hash\fR. .ie n .SS "%hash\->\fBto_ref()\fP : $hashref" .el .SS "\f(CW%hash\fP\->\fBto_ref()\fP : \f(CW$hashref\fP" .IX Subsection "%hash->to_ref() : $hashref" Return the reference to the \f(CW%hash\fR, regardless of context. .PP Useful for ensuring the last hash method return a reference while in scalar context. Typically: .PP .Vb 3 \& do_stuff( \& genre_count => $books\->group_by_count("genre")\->to_ref, \& ); .Ve .ie n .SS "%hash\->\fBto_hash()\fP : %hash" .el .SS "\f(CW%hash\fP\->\fBto_hash()\fP : \f(CW%hash\fP" .IX Subsection "%hash->to_hash() : %hash" Return the \f(CW%hash\fR, regardless of context. This is mostly useful if called on a HashRef at the end of a chain of method calls. .ie n .SS "%hash\->\fBto_array()\fP : @array | @$array" .el .SS "\f(CW%hash\fP\->\fBto_array()\fP : \f(CW@array\fP | @$array" .IX Subsection "%hash->to_array() : @array | @$array" Return the key-value pairs of the \f(CW%hash\fR as an \f(CW@array\fR, ordered by the keys. .PP Useful if you need to continue calling \f(CW@array\fR methods on it. .SH "AUTOBOX AND VANILLA PERL" .IX Header "AUTOBOX AND VANILLA PERL" .SS "Raison d'etre" .IX Subsection "Raison d'etre" autobox::Core is awesome, for a variety of reasons. .IP "\(bu" 4 It cuts down on dereferencing punctuation clutter, both by using methods on references and by using \->elements to deref arrayrefs. .IP "\(bu" 4 It makes map and grep transforms read in the same direction it's executed. .IP "\(bu" 4 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. .PP On top of this, autobox::Transform provides a few higher level methods for mapping, filtering and sorting common cases which are easier to read and write. .PP Since they are at a slightly higher semantic level, once you know them they also provide a more specific meaning than just \f(CW\*(C`map\*(C'\fR or \f(CW\*(C`grep\*(C'\fR. .PP (Compare the difference between seeing a \f(CW\*(C`map\*(C'\fR and seeing a \&\f(CW\*(C`foreach\*(C'\fR loop. Just seeing the word \f(CW\*(C`map\*(C'\fR hints at what type of thing is going on here: transforming a list into another list). .PP The methods of \f(CW\*(C`autobox::Transform\*(C'\fR 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 \&\f(CW\*(C`autobox::Core\*(C'\fR. .SS "Code Comparison" .IX Subsection "Code Comparison" These examples are only for when there's a straightforward and simple Perl equivalent. .PP .Vb 3 \& ### 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"); \& \& \& \& ### filter_by \- method call: $books are Book objects \& my $sold_out_books = [ grep { $_\->is_in_stock } @$books ]; \& my $sold_out_books = $books\->filter_by("is_in_stock"); \& my $sold_out_books = $books\->grep_by("is_in_stock"); \& \& my $books_in_library = [ grep { $_\->is_in_library($library) } @$books ]; \& my $books_in_library = $books\->filter_by([ is_in_library => $library ]); \& \& ### reject_by \- hash key: $books are book hashrefs \& my $sold_out_books = [ grep { ! $_\->{is_in_stock} } @$books ]; \& my $sold_out_books = $books\->reject_by("is_in_stock"); \& \& \& \& ### uniq_by \- method call: $books are Book objects \& my %seen; my $distinct_books = [ grep { ! %seen{ $_\->id // "" }++ } @$books ]; \& my $distinct_books = $books\->uniq_by("id"); \& \& ### uniq_by \- hash key: $books are book hashrefs \& my %seen; my $distinct_books = [ grep { ! %seen{ $_\->{id} // "" }++ } @$books ]; \& my $distinct_books = $books\->uniq_by("id"); \& \& \& #### flat \- $author\->books returns an arrayref of Books \& my $author_books = [ map { @{$_\->books} } @$authors ]; \& my $author_books = $authors\->map_by("books")\->flat; .Ve .SH "DEVELOPMENT" .IX Header "DEVELOPMENT" .SS "Author" .IX Subsection "Author" Johan Lindstrom, \f(CW\*(C`\*(C'\fR .SS "Source code" .IX Subsection "Source code" .SS "Bug reports" .IX Subsection "Bug reports" Please report any bugs or feature requests on GitHub: .PP . .SH "COPYRIGHT & LICENSE" .IX Header "COPYRIGHT & LICENSE" Copyright 2016\- Johan Lindstrom, All Rights Reserved. .PP This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.