Hi Nikita, >> On May 28, 2021, at 10:31 AM, Nikita Popov <nikita....@gmail.com >> <mailto:nikita....@gmail.com>> wrote: > >> I don't think there's much need for mutable operations. sort() and shuffle() >> would be best implemented by returning a new array instead. array_push() is >> redundant with $array[]. array_shift() and array_unshift() should never be >> used. > > Why do you say array_shift() and array_unshift() should never be used? When > I wrote the above questions the use-case I was thinking about most was > $a->unshift($value) as I use array_unshift() more than most of the other > array functions. > > Do you mean that these if applied as "methods" to an array should not be use > immutably — meaning in-place is bad but returning an array value that has > been shifted would be okay — or do you have some other reason you believe > that shifting an array is bad? Note the reason I have used them in the past > is when I need to pass an array to a function written by someone else that > expects the array to be ordered. > > Also, what about very large arrays? I assume — which could be a bad > assumption — that PHP internally can be more efficient about how it handles > array_unshift() instead of just duplicating the large array so as to add an > element at the beginning? > > Arrays only support efficient push/pop operations. Performing an > array_shift() or array_unshift() requires going through the whole array to > reindex all the keys, even though you're only adding/removing one element. In > other words, array_shift() and array_unshift() are O(n) operations, not O(1) > as one would intuitively expect. If you use shift/unshift as common > operations, you're better off using a different data-structure or > construction approach.
I appreciate your explanation regarding array_shift()/array_unshift(). It left me curious though to see how those functions and have been used in userland, and what use-cases they frequently solve. I decided to focused on array_unshift() because I have found reason to use it numerous times in the past but rarely use array_shift(). I searched some of the most popular PHP projects on Github and found the following # of uses for array_unshift() while the last project was one of yours: Phan:10 https://github.com/search?q=array_unshift+language%3APHP+repo%3Aphan%2Fphan&type=Code Phabricator:27 https://github.com/search?l=&p=1&q=array_unshift+language%3APHP+repo%3Aphacility%2Fphabricator&type=Code Laravel:8 https://github.com/search?q=array_unshift+language%3APHP+repo%3Alaravel%2Fframework&type=Code Symfony:34 https://github.com/search?q=array_unshift+language%3APHP+repo%3Asymfony%2Fsymfony&type=Code PHP-CS-Fixer:8 https://github.com/search?q=array_unshift+language%3APHP+repo%3AFriendsOfPHP%2FPHP-CS-Fixer&type=Code Composer:7 https://github.com/search?q=array_unshift+language%3APHP+repo%3Acomposer%2Fcomposer&type=Code Statamic:2 https://github.com/search?q=array_unshift+language%3APHP+repo%3Astatamic%2Fcms&type=Code Magento2:75 https://github.com/search?q=array_unshift+language%3APHP+repo%3Amagento%2Fmagento2&type=Code WordPress:22 https://github.com/search?q=array_unshift+language%3APHP+repo%3AWordPress%2Fwordpress-develop&type=Code PHPMailer:1 https://github.com/search?q=array_unshift+language%3APHP+repo%3APHPMailer%2FPHPMailer&type=Code PHPBackporter:1 https://github.com/nikic/PHP-Backporter/blob/master/lib/PHPBackporter/Converter/Closure.php#L100 <https://github.com/nikic/PHP-Backporter/blob/master/lib/PHPBackporter/Converter/Closure.php#L100> From a manual review of those usages I have found roughly the following use-case categories: ------ Testing — It seems a lot of tests use array_unshift(), for a variety of pre- and post- test uses. Path Manipulation — Such as when relative paths are exploded, the base path is inserted, and path segments imploded. Ordered Processing/Filtering — Where a list of elements are filtered to insert values to be processed first such as loading "plugins", inserting regular expressions for URL route processing, "policy modes", adding optional positional arguments to default values for a "builder" (command line, SQL, etc.), inserting custom menu items in front of default menu items, inserting higher priority middleware, insert column definitions to be displayed, inserting custom paths to look for theme template in front of default paths, inserting mime-types into a list of default acceptable mime-types, inserting directories for autoloaders, etc. Parsers/Tokenizers — When writing a parser or tokenizer and the need to insert a value or token at the head of the list. Parameter Delegation — When a function accepts a list of parameter, especially when they include variadic, and then needs to pass on the same set of parameters to another function such as with call_user_func_array(), sprints(), etc.Additional similar uses are to insert $this into return value of func_get_args() to allow delegation to a global function, also using call_user_func_array(). ------ There may be more but in general those were the main reasons I found for using array_unshift() that I am not sure could be replaced by another data structure to any advantage. It's hard to replace an array with something else when your source for the data is a function that returns an array. (BTW, Phabricator uses array_unshift() more than any other, but uses it in contexts that would be easily replaced with something more performant. Ironic given that the project is no longer being maintained.) So I was also intrigued by your statement that "you're better off using a different data-structure or construction approach" since I think we mostly use these functions when we have to use an array, not because we chose to. Such as when we are either given an array by some function we are not in control of, or when we need to pass a value to an ordered function we are not in control of. In the above use-cases I would really like to know if you can envision other data structures or constructions being better and if so what they are? Also, almost every one of the use-cases mentioned above generally deal with very short arrays, such as parameter delegation. Is O(<n>) really problematic when <n> is rather small? ----- But from all this I do agree with you that just returning an array would likely be acceptable. ----- This would not be a great solution for really large arrays, but then we can't eliminate the need for a developer to have a reasonable level of knowledge, right? So: $unshifted = $array->unshift($new_element); $shifted = $array->shift(); But, we could also possibly use better names for these: $right_shifted = $array->shift_right(...$new_element(s)); $left_shifted = $array->shift_left([element_count]); OR $prepended = $array->prepend(...$new_element(s)); $prebated = $array->prebate([element_count]); -Mike