/** * Gets an array of slices of size `$size` from an array. * ```php * $pairs = slices(2); * $pairs([1, 2, 3, 4, 5]); // [[1, 2], [3, 4], [5]] * $pairs("Hello World"); // ['He', 'll', 'o ', 'Wo', 'rl', 'd'] * slices(5, [1, 2]); // [[1, 2]] * slices(3, []) // [] * slices(3, '') // '' * ``` * * @signature Number -> [a] -> [[a]] * @signature Number -> String -> [String] * @param int $size * @param array $list * @return array */ function slices() { $slices = function ($size, $list) { if (empty($list)) { return is_string($list) ? '' : []; } if (length($list) <= $size) { return [$list]; } return prepend(take($size, $list), slices($size, remove($size, $list))); }; return apply(curry($slices), func_get_args()); }
/** * Splits a string into chunks without spliting any group surrounded with some * specified characters. `$surrounders` is a string where each pair of characters * specifies the starting and ending characters of a group that should not be split. * ```php * $groups = chunks('(){}', ','); * $groups('1,2,(3,4,5),{6,(7,8)},9'); // ['1', '2', '(3,4,5)', '{6,(7,8)}', '9'] * * $names = chunks('()""', ' '); * $names('Foo "Bar Baz" (Some other name)'); // ['Foo', 'Bar Baz', 'Some other name'] * ``` * * @signature String -> String -> String -> [String] * @param string $surrounders * @param string $separator * @param sring $text * @return array */ function chunks() { $chunks = function ($surrounders, $separator, $text) { // Let's assume some values to understand how this function works // surrounders = '""{}()' // separator = ' ' // $text = 'foo ("bar baz" alpha) beta' $surrounders = map(slices(1), slices(2, $surrounders)); // [['"'. '"'], ['{'. '}'], ['(', ')']] $openings = map(get(0), $surrounders); // ['"', '{', '('] $closings = map(get(1), $surrounders); // ['"', '}', ')'] $numOfSurrounders = length($surrounders); // 3 $indexes = keys($surrounders); // [0, 1, 2] $items = split($separator, $text); // ['foo', '("bar', 'baz"', 'alpha)', 'beta'] // The initial state $state = (object) ['chunks' => [], 'counts' => array_fill(0, $numOfSurrounders, 0), 'total' => 0]; // We will iterate over $items and update the $state while adding them // For each item we need to update counts and chunks // Updates count for a single surrender (the surrender at $index) // $item : the item we are adding // $counts : the previous counts $updateCountAt = curry(function ($item, $counts, $index) use($openings, $closings) { $count = occurences(__(), $item); return $openings[$index] == $closings[$index] ? ($counts[$index] + $count($openings[$index])) % 2 : $counts[$index] + $count($openings[$index]) - $count($closings[$index]); }); // Updates counts for all surrenders $updateCounts = curry(function ($item, $counts) use($indexes, $updateCountAt) { return map($updateCountAt($item, $counts), $indexes); }); // Adds an item to the state and returns a new state $addItem = function ($state, $item) use($separator, $updateCounts) { $counts = $updateCounts($item, get('counts', $state)); $newChunks = 0 == $state->total ? append($item, $state->chunks) : append(last($state->chunks) . $separator . $item, init($state->chunks)); return (object) ['chunks' => $newChunks, 'counts' => $counts, 'total' => sum($counts)]; }; // Returns the chunks of the resulting state after adding all items return get('chunks', reduce($addItem, $state, $items)); }; return apply(curry($chunks), func_get_args()); }