lib/PicoTwigExtension.php
public function sortByFilter($var, $sortKeyPath, $fallback = 'bottom')
    {
        if (is_object($var) && ($var instanceof Traversable)) {
            $var = iterator_to_array($var, true);
        } elseif (!is_array($var)) {
            throw new Twig_Error_Runtime(sprintf(
                'The sort_by filter only works with arrays or "Traversable", got "%s"',
                is_object($var) ? get_class($var) : gettype($var)
            ));
        }
        if (($fallback !== 'top') && ($fallback !== 'bottom') && ($fallback !== 'keep') && ($fallback !== "remove")) {
            throw new Twig_Error_Runtime(
                'The sort_by filter only supports the "top", "bottom", "keep" and "remove" fallbacks'
            );
        }

        $twigExtension = $this;
        $varKeys = array_keys($var);
        $removeItems = array();
        uksort($var, function ($a, $b) use ($twigExtension, $var, $varKeys, $sortKeyPath, $fallback, &$removeItems) {
            $aSortValue = $twigExtension->getKeyOfVar($var[$a], $sortKeyPath);
            $aSortValueNull = ($aSortValue === null);

            $bSortValue = $twigExtension->getKeyOfVar($var[$b], $sortKeyPath);
            $bSortValueNull = ($bSortValue === null);

            if (($fallback === 'remove') && ($aSortValueNull || $bSortValueNull)) {
                if ($aSortValueNull) {
                    $removeItems[$a] = $var[$a];
                }
                if ($bSortValueNull) {
                    $removeItems[$b] = $var[$b];
                }
                return ($aSortValueNull - $bSortValueNull);
            } elseif ($aSortValueNull xor $bSortValueNull) {
                if ($fallback === 'top') {
                    return ($aSortValueNull - $bSortValueNull) * -1;
                } elseif ($fallback === 'bottom') {
                    return ($aSortValueNull - $bSortValueNull);
                }
            } elseif (!$aSortValueNull && !$bSortValueNull) {
                if ($aSortValue != $bSortValue) {
                    return ($aSortValue > $bSortValue) ? 1 : -1;
                }
            }

            // never assume equality; fallback to original order
            $aIndex = array_search($a, $varKeys);
            $bIndex = array_search($b, $varKeys);
            return ($aIndex > $bIndex) ? 1 : -1;
        });

        if ($removeItems) {
            $var = array_diff_key($var, $removeItems);
        }

        return $var;
    }
public function sortByFilter($var, $sortKeyPath, $fallback = 'bottom')
    {
        if (is_object($var) && ($var instanceof Traversable)) {
            $var = iterator_to_array($var, true);
        } elseif (!is_array($var)) {
            throw new Twig_Error_Runtime(sprintf(
                'The sort_by filter only works with arrays or "Traversable", got "%s"',
                is_object($var) ? get_class($var) : gettype($var)
            ));
        }
        if (($fallback !== 'top') && ($fallback !== 'bottom') && ($fallback !== 'keep') && ($fallback !== "remove")) {
            throw new Twig_Error_Runtime(
                'The sort_by filter only supports the "top", "bottom", "keep" and "remove" fallbacks'
            );
        }

        $twigExtension = $this;
        $varKeys = array_keys($var);
        $removeItems = array();
        uksort($var, function ($a, $b) use ($twigExtension, $var, $varKeys, $sortKeyPath, $fallback, &$removeItems) {
            $aSortValue = $twigExtension->getKeyOfVar($var[$a], $sortKeyPath);
            $aSortValueNull = ($aSortValue === null);

            $bSortValue = $twigExtension->getKeyOfVar($var[$b], $sortKeyPath);
            $bSortValueNull = ($bSortValue === null);

            if (($fallback === 'remove') && ($aSortValueNull || $bSortValueNull)) {
                if ($aSortValueNull) {
                    $removeItems[$a] = $var[$a];
                }
                if ($bSortValueNull) {
                    $removeItems[$b] = $var[$b];
                }
                return ($aSortValueNull - $bSortValueNull);
            } elseif ($aSortValueNull xor $bSortValueNull) {
                if ($fallback === 'top') {
                    return ($aSortValueNull - $bSortValueNull) * -1;
                } elseif ($fallback === 'bottom') {
                    return ($aSortValueNull - $bSortValueNull);
                }
            } elseif (!$aSortValueNull && !$bSortValueNull) {
                if ($aSortValue != $bSortValue) {
                    return ($aSortValue > $bSortValue) ? 1 : -1;
                }
            }

            // never assume equality; fallback to original order
            $aIndex = array_search($a, $varKeys);
            $bIndex = array_search($b, $varKeys);
            return ($aIndex > $bIndex) ? 1 : -1;
        });

        if ($removeItems) {
            $var = array_diff_key($var, $removeItems);
        }

        return $var;
    }
public function sortByFilter ( $ var , $ sortKeyPath , $ fallback = 'bottom' ) { if ( is_object ( $ var ) && ( $ var instanceof Traversable ) ) { $ var = iterator_to_array ( $ var , true ) ; } elseif ( ! is_array ( $ var ) ) { throw new Twig_Error_Runtime ( sprintf ( 'The sort_by filter only works with arrays or "Traversable", got "%s"' , is_object ( $ var ) ? get_class ( $ var ) : gettype ( $ var ) ) ) ; } if ( ( $ fallback !== 'top' ) && ( $ fallback !== 'bottom' ) && ( $ fallback !== 'keep' ) && ( $ fallback !== "remove" ) ) { throw new Twig_Error_Runtime ( 'The sort_by filter only supports the "top", "bottom", "keep" and "remove" fallbacks' ) ; } $ twigExtension = $ this ; $ varKeys = array_keys ( $ var ) ; $ removeItems = array ( ) ; uksort ( $ var , function ( $ a , $ b ) use ( $ twigExtension , $ var , $ varKeys , $ sortKeyPath , $ fallback , & $ removeItems ) { $ aSortValue = $ twigExtension -> getKeyOfVar ( $ var [ $ a ] , $ sortKeyPath ) ; $ aSortValueNull = ( $ aSortValue === null ) ; $ bSortValue = $ twigExtension -> getKeyOfVar ( $ var [ $ b ] , $ sortKeyPath ) ; $ bSortValueNull = ( $ bSortValue === null ) ; if ( ( $ fallback === 'remove' ) && ( $ aSortValueNull || $ bSortValueNull ) ) { if ( $ aSortValueNull ) { $ removeItems [ $ a ] = $ var [ $ a ] ; } if ( $ bSortValueNull ) { $ removeItems [ $ b ] = $ var [ $ b ] ; } return ( $ aSortValueNull - $ bSortValueNull ) ; } elseif ( $ aSortValueNull xor $ bSortValueNull ) { if ( $ fallback === 'top' ) { return ( $ aSortValueNull - $ bSortValueNull ) * - 1 ; } elseif ( $ fallback === 'bottom' ) { return ( $ aSortValueNull - $ bSortValueNull ) ; } } elseif ( ! $ aSortValueNull && ! $ bSortValueNull ) { if ( $ aSortValue != $ bSortValue ) { return ( $ aSortValue > $ bSortValue ) ? 1 : - 1 ; } } // never assume equality; fallback to original order $ aIndex = array_search ( $ a , $ varKeys ) ; $ bIndex = array_search ( $ b , $ varKeys ) ; return ( $ aIndex > $ bIndex ) ? 1 : - 1 ; } ) ; if ( $ removeItems ) { $ var = array_diff_key ( $ var , $ removeItems ) ; } return $ var ; }
Sorts an array by one of its keys or a arbitrary deep sub-key

This method is registered as the Twig `sort_by` filter. You can use this
filter to e.g. sort the pages array by a arbitrary meta value. Calling
`{{ pages|sort_by([ "meta", "nav" ]) }}` returns all pages sorted by the
meta value `nav`. The sorting algorithm will never assume equality of
two values, it will then fall back to the original order. The result is
always sorted in ascending order, apply Twigs `reverse` filter to
achieve a descending order.

@param array|Traversable $var         variable to sort
@param mixed             $sortKeyPath key to use for sorting; either
a scalar or a array interpreted as key path (i.e. ['foo', 'bar']
will sort $var by $item['foo']['bar'])
@param string            $fallback    specify what to do with items
which don't contain the specified sort key; use "bottom" (default)
to move these items to the end of the sorted array, "top" to rank
them first, "keep" to keep the original order, or "remove" to remove
these items

@return array sorted array
Sorts an array by one of its keys or a arbitrary deep sub - key