skip to Main Content

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

4

Answers


  1. Chosen as BEST ANSWER
    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;
    
        } 
    

  2. 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.

    Login or Signup to reply.
  3. 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);
    
    Login or Signup to reply.
  4. 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

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search