Reformat data within a nested array

A quick solution would be to change this:

    //rename the array keys
    foreach( $data as &$new_values ) {
      $new_values['user_id'] = $new_values[0]; unset( $new_values[0] );
      $new_values['product_id'] = $new_values[1]; unset( $new_values[1] );
    }

Into this:

    //rename the array keys
    foreach( $data as &$new_values ) {
      $new_values['user_id'] = (string) $new_values[0]; unset( $new_values[0] );
      $new_values['product_id'] = (string) $new_values[1]; unset( $new_values[1] );
    }

However, I think you can shorten the work a bit by only looping once. This is based on the your code so I’m just guessing on the structure on the data retrieved from the database.

$wpdb->get_results() has a second parameter which you can use to choose how the retrieved data should be structured. In my code below I use an associative array instead of the default object. This is just a preference for me and does not really matter in this case. But if you would not need your resulting data array to be indexed by the item_id you wouldn’t have to convert your retrieved data from an objects to associative arrays with this parameter.

    global $wpdb;

    //Retrieve the form entries from the database.      
    $form_entries = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}frm_item_metas WHERE field_id in (145,147)", ARRAY_A );

    $data = array();
    // Loop through all form entries from query and add them to the data array.
    foreach ( $form_entries as $form_entry ) {
        // Make sure the item_id exist and some kind of meta value too.
        if ( ! empty( $form_entry[ 'item_id' ] ) && ! empty( $form_entry[ 'meta_value' ] ) ) {
            $item_id = $form_entry[ 'item_id' ];
            $entry_value = $form_entry[ 'meta_value' ];

            // Begin row in data array with the item_id as the numeric key.
            $data[ $item_id ] = array();
            // Add user_id if it exist.
            if ( $entry_value[ 'user_id' ] ) {
                $data[ $item_id ][ 'user_id' ] (string) $entry_value[ 'user_id' ];
            }
            // Add product_id if it exist.
            if ( $entry_value[ 'product_id' ] ) {
                $data[ $item_id ][ 'product_id' ] (string) $entry_value[ 'product_id' ];
            }
        }
    }

    // $data should now hold the structure as you described

    /*
    //rename the array keys
    foreach( $data as &$new_values ) {
      $new_values['user_id'] = $new_values[0]; unset( $new_values[0] );
      $new_values['product_id'] = $new_values[1]; unset( $new_values[1] );
    }
    unset($new_values);
    */

You might want to read up a bit about protecting queries against SQL injection attacks. Unless those two field_ids are static.