【问题标题】:Group Array Elements by Letter Range按字母范围对数组元素进行分组
【发布时间】:2017-04-12 13:50:33
【问题描述】:

我有一个简单的数组,其中包含一些名称,我想按它们的第一个字母对它们进行分组。 例如。所有以 A 到 C 作为第一个字母的名称都放在一个数组中,而 D 到 F 放在另一个数组中,依此类推。

有没有比使用大量 if else 更好的方法?

【问题讨论】:

  • 你的简单数组在哪里?用你的问题更新它
  • 只是为了澄清。这不是“排序”,而是分成桶。如果您递归地执行此操作,直到每个桶的大小为 1,然后合并桶,那么您就有了基数排序。
  • 为什么使用这种排序而不是普通排序?

标签: php arrays grouping


【解决方案1】:

我现在有四种方法可以提供。所有都可以通过更改$size 来修改以允许更大或更小的组。

  • 2 创建“AB”、“CD”等
  • 3 创建“ABC”、“DEF”等
  • 4 创建“ABCD”、“EFGH”等
  • 15 创建“ABCDEFGHIJKLMNO”、“PQRSTUVWXYZ”

Code#1 通过使用 2 个foreach() 循环和比较每个值的第一个字符将值作为数组处理。这是最容易理解的。

$fruits=array("date","guava","lemon","Orange","kiwi","Banana","apple");
natcasesort($fruits);  // pre-sort them for alphabetized output
$size=3;  // <-modify group sizes here
$chunks=array_chunk(range('A','Z'),$size);  // 0=>["A","B","C"],1=>["D","E","F"],etc...
foreach($fruits as $fruit){
    foreach($chunks as $letters){
        if(in_array(strtoupper($fruit[0]),$letters)){  // check if captialized first letter exists in $letters array
            $groups[implode($letters)][]=$fruit;  // push value into this group
            break;  // go to next fruit/value
        }
    }
}
var_export($groups);

Code#2 将 apokryfos 非常聪明的 ord() 行与 Code#1 集成在一起,以消除内部循环(以及内部循环本身)的不匹配迭代。这提高了效率,但对可读性产生了负面影响。

$fruits=array("date","guava","lemon","Orange","kiwi","Banana","apple");
natcasesort($fruits);  // pre-sort them for alphabetized output
$size=3;  // <-modify group sizes here
$chunks=array_chunk(range('A','Z'),$size);  // 0=>["A","B","C"],1=>["D","E","F"],etc...
foreach($fruits as $fruit){
    $groups[implode($chunks[floor((ord(strtoupper($fruit[0]))-ord("A"))/$size)])][]=$fruit;
}
var_export($groups);

Code#3 使用 preg_match_all() 和一些过滤函数将值处理为 csv 字符串。这假设没有任何值在其中包含逗号。在我看来,由于所有的功能和非常长的正则表达式模式,这段代码很难一眼理解。

$fruits=array("date","guava","lemon","Orange","kiwi","Banana","apple");
natcasesort($fruits);  // pre-sort them for alphabetized output  // array(6 => 'apple',5 => 'Banana',0 => 'date',1 => 'guava',4 => 'kiwi',2 => 'lemon',3 => 'Orange')
$size=3;  // <-modify group sizes here
$chunks=str_split(implode(range('A','Z')),$size);  // ['ABC','DEF','GHI','JKL','MNO','PQR','STU','VWX','YZ']
$regex="/((?<=^|,)[".implode('][^,]*)|((?<=^|,)[',$chunks)."][^,]*)/i";  // '/((?<=^|,)[ABC][^,]*)|((?<=^|,)[DEF][^,]*)|((?<=^|,)[GHI][^,]*)|((?<=^|,)[JKL][^,]*)|((?<=^|,)[MNO][^,]*)|((?<=^|,)[PQR][^,]*)|((?<=^|,)[STU][^,]*)|((?<=^|,)[VWX][^,]*)|((?<=^|,)[YZ][^,]*)/i'
if(preg_match_all($regex,implode(",",$fruits),$out)){
    $groups=array_map('array_values',   // 0-index subarray elements
        array_filter(                   // omit empty subarrays
            array_map('array_filter',   // omit empty subarray elements
                array_combine($chunks,  // use $chunks as keys for $out
                    array_slice($out,1) // remove fullstring subarray from $out
                )
            )
        )
    );
    var_export($groups);
}

代码#4 将值作为不带循环或条件的数组处理,使用:array_map()preg_grep()array_values()array_combine()array_filter 形成一个-liner *折扣$size & $chunks 声明。 ...我不想停下来,直到我制作了一个单线 - 无论多么丑陋。 ;)

$fruits=array("date","guava","lemon","Orange","kiwi","Banana","apple");
natcasesort($fruits);  // pre-sort them for alphabetized output
$size=3;  // <-modify group sizes here
$chunks=str_split(implode(range('A','Z')),$size);  // ['ABC','DEF','GHI','JKL','MNO','PQR','STU','VWX','YZ']
$groups=array_filter(array_combine($chunks,array_map(function($v)use($fruits){return array_values(preg_grep("/^[$v].*/i",$fruits));},$chunks)));
var_export($groups);


// $groups=array_filter(  // remove keys with empty subarrays
//            array_combine($chunks,  // use $chunks as keys and subarrays as values
//                array_map(function($v)use($fruits){ // check every chunk
//                    return array_values(  // reset subarray's keys
//                        preg_grep("/^[$v].*/i",$fruits)  // create subarray of matches
//                    );
//                },$chunks)
//            )
//        );

所有代码输出相同的结果:

array (
  'ABC' => 
  array (
    0 => 'apple',
    1 => 'Banana',
  ),
  'DEF' => 
  array (
    0 => 'date',
  ),
  'GHI' => 
  array (
    0 => 'guava',
  ),
  'JKL' => 
  array (
    0 => 'kiwi',
    1 => 'lemon',
  ),
  'MNO' => 
  array (
    0 => 'Orange',
  ),
)

【讨论】:

    【解决方案2】:

    你可以这样做:

    function buckets($array, callable $bucketFunc) {
        $buckets = [];
    
        foreach ($array as $val) {
            $bucket = $bucketFunc($val);
            if (!isset($buckets[$bucket])) {
                $buckets[$bucket] = [];
            }
            $buckets[$bucket][] = $val;
        }
        return $buckets;
    }
    
    function myBucketFunc($value) {
          //Gets the index of the first character and returns which triple of characters it belongs to
          return floor((ord(ucfirst($value)) - ord("A"))/3); 
    }
    $array = [ "Abc", "Cba", "Foo","Hi", "Bar" ];
    
    $buckets = buckets($array, 'myBucketFunc');//Any function would 
    

    会返回:

    Array
    (
        [0] => Array
            (
                [0] => Abc
                [1] => Cba
                [2] => Bar
            )
    
        [1] => Array
            (
                [0] => Foo
            )
    
        [2] => Array
            (
                [0] => Hi
            )
    
    )
    

    进一步说明:

    ord 返回字符的 ASCII 值。

    ord("X") - ord("A") 会返回 X 的字母索引。

    如果我们将字母表分成每个包含 3 个字母的桶,则将该字母索引除以 3 将返回 X 的桶号。

    【讨论】:

    • 我赞成这个答案,因为我发现受启发的 ord() 行很有帮助。
    【解决方案3】:

    这是以非标量方式很好地使用array_reduce

    function keyize(string $word, $stride = 3): string {
        $first = strtoupper($word{0});
        $index = (int)floor((ord($first) - ord('A'))/$stride);
        return implode('', array_chunk(range('A', 'Z'), $stride)[$index]);
    }
    
    function bucketize(array $words, $stride = 3): array {
        return array_reduce(
            $words,
            function ($index, $word) use ($stride) {
                $index[keyize($word, $stride)][] = $word;
                return $index;
            },
            []
        );
    }
    
    $words = [ 'alpha', 'Apple', 'Bravo', 'banana', 'charlie', 'Cucumber', 'echo', 'Egg', ];
    shuffle($words);
    $buckets = bucketize($words, 3); // change the number of characters you want grouped, eg 1, 13, 26
    ksort($buckets);
    var_dump($buckets);
    

    所以我们使用 array_reduce 来遍历 - 同时构建 - 桶。它不是最有效的实现,因为桶数组是通过每个闭包调用复制的。但是,它很紧凑。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-02-07
      • 1970-01-01
      • 2018-06-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-28
      相关资源
      最近更新 更多