按字母范围分组数组元素

时间:2017-04-12 13:50:34

标签: php arrays grouping

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

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

3 个答案:

答案 0 :(得分:2)

我现在有四种方法可供选择。通过更改$size可以修改所有内容以允许更大或更小的组。

  • 2创建" AB"," CD"等
  • 3创建" ABC"," DEF"等
  • 4创建" ABCD"," EFGH"等
  • 15创建" ABCDEFGHIJKLMNO"," PQRSTUVWXYZ"

代码#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);

代码#2 集成了apokryfos&#39;代码#1的非常聪明的ord()行消除了内循环(和内循环本身)的不匹配迭代。这提高了效率,但对可读性产生了负面影响。

$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);

代码#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形成一个单行*折扣$size&amp; $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',
  ),
)

答案 1 :(得分:1)

你可以这样做:

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的桶号。

答案 2 :(得分:0)

这是以非标量方式很好地使用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来行走 - 并同时构建 - 桶。它实现效率不高,因为通过每个闭包调用复制了bucket数组。但是,它很紧凑。