基于另一个数组,在二维数组的每一行中保留元素。

9
我有这个数组:
0 => array:3 [
    "product_id" => "1138"
    "product_image" => "/resources/medias/shop/products/shop-6500720--1.png"
    "product_sku" => "6500722"
  ]
1 => array:3 [
    "product_id" => "1144"
    "product_image" => "/resources/medias/shop/products/shop-6501041--1.png"
    "product_sku" => "6501046"
  ]
2 => array:3 [
    "product_id" => "113"
    "product_image" => "/resources/medias/shop/products/shop-6294909--1.png"
    "product_sku" => "6294915"
]

我希望您能够获取一个仅包含所需列的多维数组(array_column不是一个选项,因为它只给我1列)。 我已经做了什么
function colsFromArray($array, $keys)
{
    return array_map(function ($el) use ($keys) {
        return array_map(function ($c) use ($el) {
            return $el[$c];
        }, $keys);
    }, $array);
}

$array = array(
    [
        "product_id"    => "1138",
        "product_image" => "/resources/medias/shop/products/shop-6500720--1.png",
        "product_sku"   => "6500722"
    ],
    [
        "product_id"    => "1144",
        "product_image" => "/resources/medias/shop/products/shop-6501041--1.png",
        "product_sku"   => "6501046"
    ],
    [
        "product_id"    => "113",
        "product_image" => "/resources/medias/shop/products/shop-6294909--1.png",
        "product_sku"   => "6294915"
    ]
);
colsFromArray($array, array("product_id", "product_sku"));

//0 => array:3 [
//    "product_id" => "1138"
//    "product_sku" => "6500722"
//  ]
//1 => array:3 [
//    "product_id" => "1144"
//    "product_sku" => "6501046"
//  ]
//2 => array:3 [
//    "product_id" => "113"
//    "product_sku" => "6294915"
//]

问题在于它看起来太卡了,因为它对此进行了两次迭代。 有没有什么方法可以在不使用这种解决方法的情况下获得多列?
我正在使用PHP5.6

嗨@fiskolin,问题不太清楚。 - jvk
@fiskolin 你可以解释一下数组是从哪里来的吗?是否有可能修改最初获取数据的方式? - Brandon Nelson
1
更大的问题是你丢失了钥匙, 沙盒 - ArtisticPhoenix
这个问题的 Laravel 版本:如何使用 Pluck 获取多列数据? 另外一个相关话题:如何从二维数组的子数组中移除“列”? - mickmackusa
7个回答

15

如果您需要从一个数组中获取两列数据,其中一列为SKU(通常是唯一的),则可以使用array_column函数并指定第三个参数。

$new = array_column($arr, "product_id", "product_sku");

这将返回一个扁平化的数组,其中SKU是键,ID是值,使数组易于处理。

输出:

array(3) {
  [6500722]=>
  string(4) "1138"
  [6501046]=>
  string(4) "1144"
  [6294915]=>
  string(3) "113"
}

https://3v4l.org/UDGiO


这是对另一个问题的正确答案。它不能提供问题所要求的期望结果。(这并不意味着这是一种不好的技术;只是偏离了所需的要求。) - mickmackusa
如果转换为键的列在原始数组中具有重复值,则此技术将不适用。虽然这在提问者的示例数据中似乎不太可能,但研究人员应该意识到这一点。 - mickmackusa
这个答案中的建议可以在之前的Stack Overflow页面上找到多次,例如这里 - mickmackusa

6
我认为更大的问题是你会丢失钥匙。 原始代码
array (
  0 => 
  array (
    0 => '1138',
    1 => '6500722',
  ),
  1 => 
  array (
    0 => '1144',
    1 => '6501046',
  ),
  2 => 
  array (
    0 => '113',
    1 => '6294915',
 );

您可以使用简单的foreach来替代第二个array_map:

function colsFromArray(array $array, $keys)
{
    if (!is_array($keys)) $keys = [$keys];
    return array_map(function ($el) use ($keys) {
        $o = [];
        foreach($keys as $key){
            //  if(isset($el[$key]))$o[$key] = $el[$key]; //you can do it this way if you don't want to set a default for missing keys.
            $o[$key] = isset($el[$key])?$el[$key]:false;
        }
        return $o;
    }, $array);
}

输出

array (
  0 => 
  array (
    'product_id' => '1138',
    'product_sku' => '6500722',
  ),
  1 => 
  array (
    'product_id' => '1144',
    'product_sku' => '6501046',
  ),
  2 => 
  array (
    'product_id' => '113',
    'product_sku' => '6294915',
  ),
)

沙盒

问题是这似乎太卡顿了,因为它要迭代两次。

没有真正的方法可以不迭代两次,但您可能不想扔掉键。

话虽如此,您可以递归删除不需要的项。

function colsFromArray(array &$array, $keys)
{
    if (!is_array($keys)) $keys = [$keys];
    foreach ($array as $key => &$value) {
        if (is_array($value)) {
            colsFromArray($value, $keys); //recursive
        }else if(!in_array($key, $keys)){
           unset($array[$key]); 
        }
    }
}

colsFromArray($array, array("product_id", "product_sku"));
var_export($array);

与之前相同的输出

通过引用更容易实现。无论是否更快,您都需要测试这两个方法并进行比较。

沙盒

最后一点是,您不应该假设键存在,也不应该假设键是一个数组,除非您将它强制转换为数组类型。

您还可以使用数组过滤器来完成此操作

function colsFromArray(array $array, $keys)
{
    if (!is_array($keys)) $keys = [$keys];
    $filter = function($k) use ($keys){
       return in_array($k,$keys);
    };
    return array_map(function ($el) use ($keys,$filter) {
        return array_filter($el, $filter, ARRAY_FILTER_USE_KEY );
    }, $array);
}

在循环(array_map)之外声明过滤函数会带来一些性能上的小优势。

代码沙箱


6
如果您不想更改原始数组并希望获得所需的输出,请使用array_intersect_key函数获取所需的输出,如下所示。
$array = array(
    [
        "product_id"    => "1138",
        "product_image" => "/resources/medias/shop/products/shop-6500720--1.png",
        "product_sku"   => "6500722"
    ],
    [
        "product_id"    => "1144",
        "product_image" => "/resources/medias/shop/products/shop-6501041--1.png",
        "product_sku"   => "6501046"
    ],
    [
        "product_id"    => "113",
        "product_image" => "/resources/medias/shop/products/shop-6294909--1.png",
        "product_sku"   => "6294915"
    ]
);

$keys = array("product_id"=>1, "product_sku"=>2);

$filteredArray = array_map(function($a) use($keys){
    return array_intersect_key($a,$keys);
}, $array);

print_r($filteredArray);

真正的简洁解决方案,你也可以使用 array_flip() 将数组值作为键来过滤! - Othyn

4
我将优雅的方法从@Chayan重构为一个函数,这样就可以像array_column()一样使用。要过滤的键现在可以表示为一个简单的数组。
顺便说一下,这很可能也是最快的方法,因为它使用内置函数来完成大部分繁重的工作。
function array_columns(array $arr, array $keysSelect)
{    
    $keys = array_flip($keysSelect);
    return array_map(
        function($a) use($keys) {
            return array_intersect_key($a,$keys);
        },
        $arr
    );
}

$arr = [
    [
        "product_id"    => "1138",
        "product_image" => "/resources/medias/shop/products/shop-6500720--1.png",
        "product_sku"   => "6500722"
    ],
    [
        "product_id"    => "1144",
        "product_image" => "/resources/medias/shop/products/shop-6501041--1.png",
        "product_sku"   => "6501046"
    ],
    [
        "product_id"    => "113",
        "product_image" => "/resources/medias/shop/products/shop-6294909--1.png",
        "product_sku"   => "6294915"
    ]
];

$keysSelect = ["product_id" , "product_sku"];
$filteredArray = array_columns($arr, $keysSelect);

var_dump($filteredArray);

1

如果我正确理解了您的问题,您可以尝试使用传统的foreach循环-它可能会快一些。

function colsFromArray($array, $filterKeys) {
    $newArr = [];
    foreach($array as $val) {
       $element = [];
       foreach($filterKeys as $filterKey) {
          $element[$filterKey] = $val[$filterKey];
       }
       $newArr[] = $element;
    }
}

(未测试)

问题在于它似乎太卡了,因为它对此进行了两次迭代

您的原始代码并没有在同一个数组上进行两次迭代。如果您想要一个数组,其中每个元素都是来自filterKeys数组的键的另一个元素数组,则无法避免在主数组和filterKeys数组上进行迭代。


0

对于 array_columns 函数的一个新增功能,最终追溯到 Chayan 的答案,这次是从 Joseph Mangion 的函数扩展而来。

我偶尔会有一些长列表,其中包含我想要保留键并且不一定想要遵循繁琐的 ['orignal_field_name'] => ['original_field_name'] 格式的大量字段。

此版本默认情况下保留每个字段的原始键,除非指定了新键。

// See answer from Joseph Mangion: https://dev59.com/K67la4cB1Zd3GeqPlfPe
/** Function - array_columns  Selects columns from multidimensional array and renames columns as required
*
* @param  array $in_array, array $select_columns_rename_keys
*   example of $select_columns_rename_keys:
*       ['new_column_name1' => 'original_column_name1', 'original_column_name2', 'original_column_name3', 'new_column_name4' => 'original_column_name4', ...]
*       This will use the original keys for columns 2 and 3 and rename columns 1 and 4
* @return array
* @access public
* 
*/   

public function array_columns($in_array, $select_columns_rename_keys) {
    foreach ($select_columns_rename_keys as $k => $v)
        if (is_int($k)) {
            $select_columns_rename_keys[$v] = $v;
            unset($select_columns_rename_keys[$k]);
        }
    $keys = array_flip($select_columns_rename_keys);
    $filtered_array =
        array_map(function($a) use($keys) {
        $data = array_intersect_key($a, $keys);
        $return_array = [];
        foreach ($data as $column_name => $value) $return_array[$keys[$column_name]] = $value;
        return $return_array;
    }, $in_array);

    return $filtered_array;
}

0

这是一个重构后的函数,基于Chayan的函数,并添加了选定列的重命名:


 /** Function - array_columns  Selects columns from multidimantional array and renames columns as required
 *
 * @param  array $arr, array $selectColRenameKeys 
 *            example: (NewName1->colNameneeded1,NewName2->colNameneeded2,ect...)
 * @return array
 * @access public
 * 
 */   

 private function array_columns( $arr,$selectColRenameKeys) {    
    $keys = array_flip($selectColRenameKeys);
    $filteredArray = array_map(function($a) use($keys){
                                  $data = array_intersect_key($a,$keys);
                                  $rename_arr= array();
                                  foreach ($data as $colname => $value){
                                    $r_arr[$keys[$colname]]= $value   ;
                                  }
                                  return $r_arr;
                               }, $arr);

    return $filteredArray;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接