【问题标题】:PHP custom sort array of strings by multiple criteriaPHP通过多个条件自定义排序字符串数组
【发布时间】:2017-05-26 19:58:48
【问题描述】:

我有一组字体名称,这些名称先是宽度,然后是重量(按字母顺序)。标准 width 在字符串中没有像其他宽度那样的指示符(压缩、扩展等)。这是我得到数组时的样子:

Array ( 
[0] => Bold 
[1] => ExtraBold 
[2] => ExtraLight 
[3] => Light 
[4] => Medium 
[5] => Regular 
[6] => SemiBold 
[7] => Thin 
[8] => Condensed Bold 
[9] => Condensed ExtraBold 
[10] => Condensed ExtraLight 
[11] => Condensed Light 
[12] => Condensed Medium 
[13] => Condensed Regular 
[14] => Condensed SemiBold 
[15] => Condensed Thin 
[16] => Expanded Black 
[17] => Expanded Bold 
[18] => Expanded ExtraBold 
[19] => Expanded ExtraLight 
[20] => Expanded Light 
[21] => Expanded Medium 
[22] => Expanded Regular 
[23] => Expanded SemiBold 
[24] => Expanded Thin) 

然后我需要先按照这个宽度顺序进行排序:

$order_array_crit_one = array("Expanded", "Standard", "Condensed");

然后按重量依次为:

$order_array_crit_two = array("Black", "ExtraBold", "Bold", "SemiBold", "Medium", "Regular", "Light", "Thin", "ExtraLight");

我使用了一个比较单词的排序函数 (like this) 来解决这个问题,但到目前为止我提出的每个解决方案都很庞大且令人困惑。

【问题讨论】:

  • 所以在这个例子中,我可以假设“Bold”与“Standard Bold”相同吗?
  • 这可能很容易使用函数 usort php.net/usort

标签: php arrays string sorting


【解决方案1】:

比较复杂的问题在于它使 usort 效率不高,因为它必须多次转换和评估项目。但是,如果您之前以使用基本排序的方式转换数组,则可以减少这项工作。一个想法是重建数组,但这次使用计算出的数字键:

// 3 items need 2 bits to be represented:
// ( 0 => 00 => "Expanded", 1 => 01 => "Standard", 2 => 10 => "Condensed" )
$crit1 = ["Expanded", "Standard", "Condensed"];
// 9 items need 4 bits to be represented:
// ( 0 => 0000 => "Black", ... 8 => 1000 => "ExtraLight" )
$crit2 = ["Black", "ExtraBold", "Bold", "SemiBold", "Medium", "Regular", "Light", "Thin", "ExtraLight"];
// if you join all the bits, each item of your array can be represented with a 6
// bits number:
// ( 0 => 000000 => "Expanded Black" ... 40 => 101000 => "Condensed ExtraLight" )

$crit2 = array_flip($crit2);

$result = [];

foreach ($arr as $item) {
    if (false !== strpos($item, "Expanded"))
        $key = 0;  // 000000
    elseif (false !== strpos($item, "Condensed"))
        $key = 32; // 100000
    else
        $key = 16; // 010000

    $parts = explode(' ', $item);
    $weight = isset($parts[1]) ? $parts[1] : $parts[0];
    $key += $crit2[$weight];

    $result[$key] = $item;
}

ksort($result, SORT_NUMERIC);
$result = array_values($result);

print_r($result);

demo

【讨论】:

  • 感谢您的演示链接。这已经比我的简洁多了。
  • @user1063020:这段代码的重要之处在于新数组的键是一次性计算的,而不是每次都需要它们与其他项目进行比较时。简洁并不重要。
【解决方案2】:

为什么不创建一个 FontName 对象数组,它可以包含定义的属性,例如宽度、重量、长度,以及任何你想要的。然后您可以选择按该特定属性进行排序。

//pseudo-code

class FontName {
  //member variables - can use getters and setters if you want
  name;
  width;
  weight;
  length;
}

sort(FontNameArray, propertyname) {
   // use reflection to get the value of propertyname from the FontName object
   // sort by that value
}

【讨论】:

    【解决方案3】:

    假设“粗体”与“标准粗体”相同,并且您不能按照其他答案中的建议更改输入数组,这将起作用:

    $font_details = function($font) use ($order_array_crit_one, $order_array_crit_two) {
        $matches = explode(' ', $font);
        $details = new StdClass();
    
        $details->crit_one = (count($matches) > 1) ? $matches[0] : 'Standard';
        $details->crit_one_position = array_search($details->crit_one, $order_array_crit_one);
        $details->crit_two = (count($matches) > 1) ? $matches[1] : $matches[0];
        $details->crit_two_position = array_search($details->crit_two, $order_array_crit_two);
    
        return $details;
    };
    
    // $fonts is your input array $fonts = array('Bold', 'ExtraBold',...
    usort($fonts, function($a, $b) use($font_details) {
        $a_details = $font_details($a);
        $b_details = $font_details($b);
    
        if($a_details->crit_one_position < $b_details->crit_one_position) return -1;
        if($a_details->crit_one_position > $b_details->crit_one_position) return 1;
        // ELSE, criteria one is the same for both - we need to look at criteria two:
        if($a_details->crit_two_position < $b_details->crit_two_position) return -1;
        if($a_details->crit_two_position > $b_details->crit_two_position) return 1;
        // ELSE both criteria are the same (same font?)
        return 0;
    });
    
    print_r($fonts); // should be sorted!
    

    希望这会有所帮助!

    【讨论】:

      【解决方案4】:

      这个问题一直困扰着我,所以我想出了另一个选择。这是一个通用且可重用的解决方案,适用于任何标准。 (至少我能想到的。)你可以找到demo here。

      <?php
      
      /**
       *  Sorts an array based on matching multiple criteria.
       *
       *  Given:
       *  $array => ['big cat', 'small dog', 'big dog', 'small cat']
       *  $criteria => [['dog','cat'], ['big','small']]
       *
       *  Result: (biggest to smallest)
       *  ['big dog', 'small dog', 'big cat', 'small cat'] 
       */
      function multi_sort(&$array, $criteria, $defaults=array()){
          $cache = array();
      
          // prepare the criteria by sorting them from longest to shortest 
          // maintaining the original key (index)
          foreach($criteria as &$c){
              uasort($c, function($a,$b){
                  return  strlen($b) - strlen($a);
              });
          }
      
          // define a function for returning the index matching the given str
          // given: 'one' and ['zero', 'one', 'two'] returns 1
          $findIndex = function($str, $values){
              foreach($values as $index=>$value){
                  if( stripos($str, $value) !== FALSE ){
                      return $index;
                  }
              }
              return NULL;
          };
      
          // define a function to calculate a weighted value based on the criteria
          // returns a value similar to: 2000-0000-3300 (one segment for each criteria)
          $calculateValue = function($str) use ($criteria, $findIndex, $defaults, $cache){
              if( !isset($cache[$str]) ){
                  $parts = array();
                  foreach($criteria as $i=>$c){
                      $parts[$i] = $findIndex($str, $c);
                      if( $parts[$i] === NULL ){
                          $parts[$i] = (isset($defaults[$i]) ? $defaults[$i] : 1000);
                      }
                      $parts[$i] = str_pad($parts[$i], 4, '0');
                  }
                  $cache[$str] = implode($parts, '-');
              }
              return $cache[$str];
          };
      
          // define our compare function
          $compare = function($a, $b) use ($calculateValue){
              $av = $calculateValue($a);
              $bv = $calculateValue($b);
              return $av > $bv;
          };
      
          // sort the array`
          usort($array, $compare);
      }
      
      $list = array(
          'Bold', 'ExtraBold', 'ExtraLight', 'Light', 'Medium', 'Regular', 'SemiBold', 'Thin', 'Condensed Bold', 'Expanded Black', 'Condensed ExtraLight', 'Expanded Thin'
      );
      
      // create our sort criteria.
      $sort_criteria = array(
          array("Expanded", "Standard", "Condensed"),
          array("Black", "ExtraBold", "Bold", "SemiBold", "Medium", "Regular", "Light", "Thin", "ExtraLight")
      );
      // sort our array; default for criteria 1 is 1 (i.e. Standard)
      multi_sort($list, $sort_criteria, array(1));
      print_r($list);
      
      // lets sort some animals from biggest to smallest.
      $animals = ['big cat', 'small dog', 'big dog', 'small cat'];
      $sort_criteria = [['dog','cat'], ['big','small']];
      multi_sort($animals, $sort_criteria);
      print_r($animals);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-05-29
        • 2019-08-30
        • 1970-01-01
        • 1970-01-01
        • 2013-05-08
        • 2016-08-02
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多