-2

I need to make order in the data structure of my product attributes in woocommerce, grouping each attribute and its values.

My array now

array (size=5)
  0 => 
    array (size=1)
      'pa_color' => 
        array (size=1)
          0 => string 'red' (length=3)
  1 => 
    array (size=1)
      'pa_color' => 
        array (size=1)
          0 => string 'red' (length=3)
  2 => 
    array (size=2)
      'pa_color' => 
        array (size=1)
          0 => string 'gray' (length=4)
      'pa_modello' => 
        array (size=1)
          0 => string 'modello2' (length=8)
  3 => 
    array (size=1)
      'pa_color' => 
        array (size=1)
          0 => string 'yellow' (length=6)
  4 => 
    array (size=0)
      empty

I need to merge in something like:

array (size=1)
      'pa_color' => 
        array (size=1)
          0 => string 'red' (length=3)
          1 => string 'gray' (length=4)
          2 => string 'yellow' (length=6)
       
       'pa_modello' => 
            array (size=1)
              0 => string 'modello2' (length=8)

grouping the values of the same keys in one array.

thanks in advance

Gozer
  • 11
  • 10

4 Answers4

0

Let $array is your array:

foreach ($array as $item)
{
   if ($item['pa_color']['0']) $color[ $item['pa_color']['0'] ] = $item['pa_color']['0'];
   if ($item['pa_modello']['0']) $modello[$item['pa_modello']['0']] = $item['pa_modello']['0'];
// this loop will remove the duplicated values and I Assume each pa_color , pa_modello in the original array contains only one element with 0 as a key
}
foreach ($color as $c) $newArray['pa_color'][] = $c;
foreach ($modello as $m) $newArray['pa_modello'][] = $m;

This should do the job.

EDIT:

For dynamic attributes comment:

foreach ($array as $item)
   foreach (array_keys($item) as $key) // getting the keys ex: pa_color 
   {
      if ($item[$key]['0']) {
         $temparray[$key][ $item[$key]['0'] ] = $item[$key]['0'];
      }
      // this loop will remove the duplicated values and I Assume each key in the original array contains only one element with 0 as a key
// the temparray will contain in its keys ($key) the pa_color , pa_module.. etc 
// each of them will be an array with the key=value approach .. in case having two "red" it will not duplicate 
   }
// finally making it zero based array 
foreach ($temparray as $key=>$item) // ex: $temparray['pa_color']['red'] = 'red'
   foreach ($item as $value) // ex: $item['red'] = 'red'
      $newArray[$key][] = $value; // ex: $newArray['pa_color'][0] = 'red';

It looks complicated .. I can't find a better approach.

Alaa Morad
  • 445
  • 4
  • 13
0
global $wp_query;
    $obj = get_queried_object();
    foreach ($wp_query->posts as $_post) {

        $_product = wc_get_product($_post->ID);
        $wc_attr_objs = $_product->get_attributes();
        $prod_attrs = [];

        foreach ($wc_attr_objs as $wc_attr => $wc_term_objs) {
            $prod_attrs[$wc_attr] = [];

            $wc_terms = $wc_term_objs->get_terms();


            foreach ($wc_terms as $wc_term) {
                 array_push($prod_attrs[$wc_attr], $wc_term->slug);


                var_dump($prod_attrs);


             }
        }

      $totals[] = $prod_attrs;

    } 
Gozer
  • 11
  • 10
  • Code-only answers are low-value on Stack Overflow because they miss an excellent opportunity to educate future researchers. – mickmackusa Jun 23 '21 at 22:58
  • Showing how your data was actually being generated would have been good to know in the question. This way answers could be tailored to your application instead of being general-use techniques. – mickmackusa Jun 24 '21 at 00:27
0

Loop through the first level of entries, then added a nest loop to create variables from the desired keys and values from the subarrays.

Because your deepest subarray only has one element in it, you can use "array destructuring" inside of the nested loop to assign the lone element value as $value.

Ensure unique values in the output by declaring the $value as the deep value's key in the result array.

If you truly want the subarrays to be re-indexed, you can use array_map() and array_values() for this -- but only do this if necessary.

Code: (Demo)

$result = [];
foreach ($array as $entries) {
    foreach ($entries as $key => [$value]) {
        $result[$key][$value] = $value;
    }
}

var_export($result);

Output:

array (
  'pa_color' => 
  array (
    'red' => 'red',
    'gray' => 'gray',
    'yellow' => 'yellow',
  ),
  'pa_modello' => 
  array (
    'modello2' => 'modello2',
  ),
)

p.s. If your deep subarrays might have more than one element, then just accommodate that with another loop to iterate that level.

$result = [];
foreach ($array as $entries) {
    foreach ($entries as $key => $values) {
        foreach ($values as $value) {
            $result[$key][$value] = $value;
        }
    }
}

Translated to the code that you wrote in your self-answering post, I think it might look like this:

global $wp_query;
$prod_attrs = [];
foreach ($wp_query->posts as $_post) {
    foreach (wc_get_product($_post->ID)->get_attributes() as $wc_attr => $wc_term_objs) {
        foreach ($wc_term_objs->get_terms() as $wc_term) {
             $prod_attrs[$wc_attr][$wc_term->slug] = $wc_term->slug;
         }
    }
}
var_export($prod_attr);
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
0

Th easiest way to combine the array would be to use array_merge_recursive with the splat operator (...) on the array of arrays.

The splat operator would unpack the array that can be used to merge them recursively.

$expected = array_merge_recursive(...$array);

Also if you need only unique values in the merged array you can use array_map like this

$unique = array_map('array_unique', $expected);

more about splat operator

Edit after comments

Because I have been heavily using this approach myself I made some tests to see which one is faster.

Sharing the results:

Question: Which is faster between the two methods?

Method 1: array_map('array_unique', array_merge_recursive(...$array));
Method 2: Using foreach as explained in mickmackusa's answer

Testing with arrays sized 5, 50, 100 and 500
Looping each function by 10000 and 100000.

T1 is time taken by array_merge_recursive and T2 is time taken by foreach

array_size loop_count T1 T2 faster diff
5 10,000 0.041 0.0206 foreach 0.0204
5 100,000 0.2061 0.2082 array_merge_recursive 0.002
5 500,000 1.0315 1.0611 array_merge_recursive 0.0296
50 10,000 0.046 0.1878 array_merge_recursive 0.1418
50 100,000 0.4452 1.8877 array_merge_recursive 1.4425
100 10,000 0.0697 0.3729 array_merge_recursive 0.3032
100 100,000 0.6795 3.7464 array_merge_recursive 3.0669
500 10,000 0.2542 1.8674 array_merge_recursive 1.6132
500 100,000 2.5359 18.6922 array_merge_recursive 16.1562

Conclusion:

  1. For Small arrays it does not matter what you use. I noticed a difference only after 500,000 loops
  2. The difference is when you use bigger arrays. When working with an array of count 500 foreach takes 16.1562 secs more

Therefore, if you have smaller arrays use whatever you want. For bigger arrays definitely avoid foreach and use array_merge_recursive

Link to test sandbox

endeavour
  • 576
  • 4
  • 15
  • 1
    I also considered `array_merge_recursive()` with the splat operator (https://stackoverflow.com/a/53028357/2943403), but it does not filter out duplicates so it needs another set of looping to provide what is required. https://3v4l.org/nVCEU This is why I did not suggest this concise technique. The OP didn't show the data-populating script in the question -- it would be better to create the desired structure before `$array` is created. This is not your fault, it was not an ideally posted question. – mickmackusa Jun 24 '21 at 00:25
  • @mickmackusa Yes it does not remove duplicates, that is why I offered the `array_unique` option. – endeavour Jun 24 '21 at 04:29
  • Yes, I understand. I am saying that having to re-iterate the results is avoidable with a different solution (like the one in my answer). The OP is having to make WP function calls to collect this data, so it will make more sense to store unique data during the initial iterating versus creating `$array` (the unwanted data structure) then calling the recursive function, then looping a final time to remove duplicates. – mickmackusa Jun 24 '21 at 04:30
  • @mickmackusa On the other question you say _funky alternative (which I can't imagine actually putting to use unless I was writing code for someone whom I didn't care for)_ Why do you say that, are there any negatives of using this approach. I like being able to do this in just one or two lines. Makes it readable – endeavour Jun 24 '21 at 04:30
  • I find the use of a recursive function call on a data structure that has finite depth to be ill-suited. (just my personal preference) It is _funky_ because I believe it is less readable/intuitive and more abstract. – mickmackusa Jun 24 '21 at 04:33
  • @mickmackusa Yes you are right using recursion on a finite depth array is not ideal. I read somewhere on SO that inbuilt php functions are faster than manually traversing an array. I cannot quote though. It could be my inference too, but I would love to be corrected if am wrong. Have not tested myself. – endeavour Jun 24 '21 at 04:41
  • There may be some rare cases where functional iteration might perform better on particular volumes of data while performing particular processes, but in the great majority of cases "language construct" loops outperform functional loops. These functions come with an inherit overhead. The benefit of using functional iterators is that they are expressive/self-documenting. – mickmackusa Jun 24 '21 at 04:58
  • Thank you, right now I am working on a system that is heavily dependent on traversing arrays - back and forth multiple times. Will keep that in mind when working on the next function – endeavour Jun 24 '21 at 05:01
  • @mickmackusa made some tests between the two methods. Edited answer based on the test results. – endeavour Jun 25 '21 at 03:32