Re: [PHP-DEV] Discussion before submission of array_transpose() RFC

2022-04-25 Thread mickmackusa
On Sun, Apr 24, 2022 at 10:24 PM Rowan Tommins  wrote:
>
> On 23/04/2022 23:42, mickmackusa wrote:
> > Ideally, PHP should have a native transposing function to put
> > developers out of their misery -- much like array_key_last() did.
>
>
> I think a better comparison would be array_column -
> https://wiki.php.net/rfc/array_column One interesting thing that Ben
> Ramsey wrote alongside that RFC was a pure PHP implementation with
> identical behaviour, available for use as a polyfill:
> https://github.com/ramsey/array_column
>
> Unlike array_column, I don't recall personally needing an
> array_transpose function, but am willing to believe that many people do.
>
>
> My first piece of advice would be to start with a definition of what you
> mean by "transpose" in this context, with an example of input and output
> in the basic case, and then a few scenarios where it's useful.
>
> Having that clear would then help understand the details you go into,
> each of which could be accompanied by its own example. For instance:
>
>
> > Let's create an intuitive transposing function for data sets with a
> > minimum depth of 2 levels.
>
>
> I can't picture what "transpose" means for something with more than 2
> levels.
>
>
> > As a matter of flexibility, there should be no requirement that the
> > lone parameter be a matrix or dense data structure.
>
>
> I have no idea what this means.
>
>
> > It should also preserve keys to give it a clear advantage over
> > pre-existing functional techniques.
>
>
> Preserve what keys?
>
>
> >  // [['single' => 'row']] becomes ['single' => 'row'] but should be
> > ['single' => ['row']]
>
>
> Should it? Why?
>
>
> My second piece of advice is to remember that flexibility, ease of use,
> and efficiency, are three corners of a triangle, and you can't optimise
> for all three. array_column does a reasonable job of covering multiple
> use cases, but there have been several unsuccessful requests over the
> years to enhance it or create variations for other use cases, so you're
> never going to make everyone happy.
>
>
> > Should the function unconditionally return an array of arrays?
> > Should it preserve the initial data types of the first and second level?
> > Should data type preservation be excluded to increase the chances of
> > its implementation?
> > [...]
> >  1. Unconditionally cast output as array of arrays.
> > [...]
> >  2. Preserve original data types of level one and level two.
> > https://3v4l.org/RRKlr
> >
> > (If input is two dimensional, then the first occurring data type in
> > the second level will dictate the result's first level data type.)
> > [...]
> > If only initially implemented to return an array of arrays, then
> > expanding the result data types in the future may be of interest.
>
>
> Without really understanding what you're saying, my suspicion is that
> these paragraphs are trying too hard to make a Swiss-army knife rather
> than a sharp blade.
>
>
> > List of Stack Overflow pages which seek to implement transposition
>
>
> Would all of these requirements be satisfied by the same implementation,
> without needing a complex set of options? What can we learn from the
> links about what features are likely to be most useful to people?
>
>
> I look forward to seeing a draft RFC, which can take the time to explain
> the features you think are needed.
>
>
> Regards,
>
> --
> Rowan Tommins
> [IMSoP]
>

Thanks for the feedback, Rowan.  Here are some answers and thoughts...

The basic premise of transposition is to replace the first level keys
with the second level keys.
This can be visualized as converting columns of data into rows of data.

With:

A B C
D E F
G H I

the expected result is:

A D G
B E H
C F I

When dealing with an indexed array of indexed arrays with more than
one row, `array_map(null, ...$array)` is a beautiful, concise
technique.
However, an array with a single row returns an unexpected structure,
and associative arrays and objects cause trouble.
Using nested foreach loops is reliable, but is less elegant, requires
multiple temporary variables, certainly more verbose, and must be
wrapped in a custom function for functional-style coding.

Like array_column()'s ability to respect object data, I felt it might
be beneficial to allow/respect objects with this function proposal.
I've made a judgment call to swap the data types of the first level
with the second level, but perhaps data types should be honored, yet
not swapped.
With my type-preserving implementation, an array of objects will
become an object of arrays.
Perhaps an array of objects should remain an array of objects after
transposition?
I'm open to feedback on this behavior.
If there is no support for any type preservation (swiss army knife)
and the function should unconditionally return an array of arrays,
then it will improve performance to use the "basic" implementation for
the RFC.
I don't program in C so I don't know if there are any optimizations
that can be enjoyed.


Re: [PHP-DEV] Discussion before submission of array_transpose() RFC

2022-04-24 Thread Marco Pivetta
Hey,

I find it interesting that you linked
https://github.com/josefernandotrindade/array_transpose/blob/master/transpose.php

Wouldn't it be better to take that, brush it up (with some tests and
benchmarks), and release it as a packagist library?
Alternatively, adding it to a high quality library such as
https://github.com/azjezz/psl

I personally don't see a reason to have this in core, let alone implemented
in C.

P.S.: sorry for dropping all the quoted messages, but ML just reported that
your message contained a lot of additional markup, and therefore my
previous message got rejected.

Marco Pivetta

https://twitter.com/Ocramius

https://ocramius.github.io/


Re: [PHP-DEV] Discussion before submission of array_transpose() RFC

2022-04-24 Thread Rowan Tommins

On 23/04/2022 23:42, mickmackusa wrote:

Ideally, PHP should have a native transposing function to put
developers out of their misery -- much like array_key_last() did.



I think a better comparison would be array_column - 
https://wiki.php.net/rfc/array_column One interesting thing that Ben 
Ramsey wrote alongside that RFC was a pure PHP implementation with 
identical behaviour, available for use as a polyfill: 
https://github.com/ramsey/array_column


Unlike array_column, I don't recall personally needing an 
array_transpose function, but am willing to believe that many people do.



My first piece of advice would be to start with a definition of what you 
mean by "transpose" in this context, with an example of input and output 
in the basic case, and then a few scenarios where it's useful.


Having that clear would then help understand the details you go into, 
each of which could be accompanied by its own example. For instance:




Let's create an intuitive transposing function for data sets with a
minimum depth of 2 levels.



I can't picture what "transpose" means for something with more than 2 
levels.




As a matter of flexibility, there should be no requirement that the
lone parameter be a matrix or dense data structure.



I have no idea what this means.



It should also preserve keys to give it a clear advantage over
pre-existing functional techniques.



Preserve what keys?



 // [['single' => 'row']] becomes ['single' => 'row'] but should be
['single' => ['row']]



Should it? Why?


My second piece of advice is to remember that flexibility, ease of use, 
and efficiency, are three corners of a triangle, and you can't optimise 
for all three. array_column does a reasonable job of covering multiple 
use cases, but there have been several unsuccessful requests over the 
years to enhance it or create variations for other use cases, so you're 
never going to make everyone happy.




Should the function unconditionally return an array of arrays?
Should it preserve the initial data types of the first and second level?
Should data type preservation be excluded to increase the chances of
its implementation?
[...]
 1. Unconditionally cast output as array of arrays.
[...]
 2. Preserve original data types of level one and level two.
https://3v4l.org/RRKlr

(If input is two dimensional, then the first occurring data type in
the second level will dictate the result's first level data type.)
[...]
If only initially implemented to return an array of arrays, then
expanding the result data types in the future may be of interest.



Without really understanding what you're saying, my suspicion is that 
these paragraphs are trying too hard to make a Swiss-army knife rather 
than a sharp blade.




List of Stack Overflow pages which seek to implement transposition



Would all of these requirements be satisfied by the same implementation, 
without needing a complex set of options? What can we learn from the 
links about what features are likely to be most useful to people?



I look forward to seeing a draft RFC, which can take the time to explain 
the features you think are needed.



Regards,

--
Rowan Tommins
[IMSoP]

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php



[PHP-DEV] Discussion before submission of array_transpose() RFC

2022-04-23 Thread mickmackusa
I am seeking constructive feedback/advice that will give me the best
chance of a successful RFC when I eventually submit it.

Introduction:

PHP developers have had an appetite for transposing data for many
years; this is indicated by the nearly 100 Stack Overflow pages on
this narrow topic.
There are many non-native techniques being used to transpose
multidimensional data sets, but each of them are some combination of
verbose, convoluted, restrictive, and/or even flawed.
Transposing comes in handy when re-orientating data for server-side
storage or graphical/tabular display.
Ideally, PHP should have a native transposing function to put
developers out of their misery -- much like array_key_last() did.


Proposal:

Let's create an intuitive transposing function for data sets with a
minimum depth of 2 levels.
The function should also return the input parameter without complaint
when the input parameter is an empty array/object.
The previous two points are purposely stated so that this function can
accommodate fetch-all result sets from database queries (e.g.
array_transpose($stmt->fetchAll(PDO::FETCH_ASSOC));).
As a matter of flexibility, there should be no requirement that the
lone parameter be a matrix or dense data structure.
It should also preserve keys to give it a clear advantage over
pre-existing functional techniques.
The function must never receive or return a non-empty one-dimensional array.
A potential consideration is to preserve data types while transposing
-- see implementation #2 and its demo link with test data.


Background:

Here are some of the popular techniques that this new function would replace:


array_unshift($array, null);
return call_user_func_array('array_map', $array);
// outdated, unintuitive, unreliable, requires non-numeric
first-level keys, destroys associative keys
// [['single' => 'row']] becomes ['single' => 'row'] but should be
['single' => ['row']]

return array_map(null, ...$array);
// unintuitive, unreliable, requires non-numeric first-level keys,
destroys associative keys
// [['single' => 'row']] becomes ['single' => 'row'] but should be
['single' => ['row']]

$out = [];
foreach ($arr as $key => $subarr) {
foreach ($subarr as $subkey => $subvalue) {
$out[$subkey][$key] = $subvalue;
}
}
return $out;
// reliable, but verbose and not functional-style

$keys = array_keys($array);
return array_map(function($array) use ($keys) {
return array_combine($keys, $array);
}, array_map(null, ...array_values($array)));
// convoluted, unintuitive, requires input to be a matrix
// [['single' => 'row']] generates Warning: array_combine()
expects parameter 2 to be array, string given"


Considerations:

Should the function unconditionally return an array of arrays?
Should it preserve the initial data types of the first and second level?
Should data type preservation be excluded to increase the chances of
its implementation?


Rough implementations of behavior:

1. Unconditionally cast output as array of arrays.

function array_transpose(array|object $object_or_array): array|object {
$levelOneType = gettype($object_or_array);
if (!in_array($levelOneType, ['array', 'object'])) {
throw new Exception("Error: array_transpose() expects
parameter 1 to be an array or object, $levelOneType given");
}
$result = [];
foreach ($object_or_array as $rowKey => $row) {
$levelTwoType = gettype($row);
if (!in_array($levelTwoType, ['array', 'object'])) {
throw new Exception("Error: array_transpose()
expects parameter 1 to contain rows of arrays or objects,
$levelTwoType given");
}
foreach ($row as $columnKey => $value) {
$result[$columnKey][$rowKey] = $value;
}
}
return $result;
}

2. Preserve original data types of level one and level two.
https://3v4l.org/RRKlr

(If input is two dimensional, then the first occurring data type in
the second level will dictate the result's first level data type.)


function array_transpose(array|object $object_or_array): array|object {
$levelOneType = gettype($object_or_array);
if (!in_array($levelOneType, ['array', 'object'])) {
throw new Exception("Fatal error: Uncaught TypeError:
array_transpose(): Argument #1 ($object_or_array) must be of type
object|array, $levelOneType given");
}
foreach ($object_or_array as $rowKey => $row) {
$levelTwoType = gettype($row);
if (!in_array($levelTwoType, ['array', 'object'])) {
throw new Exception("Fatal error: Uncaught
TypeError: array_transpose(): Argument #1 ($object_or_array) must
contain rows of type object|array, $levelTwoType given");
}