2010-09-02 92 views
14

我有以下列格式的数组:在PHP数组中合并重叠范围?

array(
    0 => array(1, 5), 
    1 => array(4, 8), 
    2 => array(19, 24), 
    3 => array(6, 9), 
    4 => array(11, 17), 
); 

其中每个项目是一个X-到-Y范围。我想合并阵列中的重叠范围,以获得更类似的内容:

array(
    0 => array(1, 9), // 1-5, 4-8 and 6-9 are overlapping, so they are merged 
    1 => array(11, 17), 
    2 => array(19, 24), 
); 

要达到此目的的最佳方法是什么?

回答

18

未经测试,但此处的想法是首先按第一个元素对数据进行排序,然后尽可能长地将后续元素与前一个元素合并。

usort($data, function($a, $b) 
{ 
     return $a[0] - $b[0]; 
}); 

$n = 0; $len = count($data); 
for ($i = 1; $i < $len; ++$i) 
{ 
     if ($data[$i][0] > $data[$n][1] + 1) 
       $n = $i; 
     else 
     { 
       if ($data[$n][1] < $data[$i][1]) 
         $data[$n][1] = $data[$i][1]; 
       unset($data[$i]); 
     } 
} 

$data = array_values($data); 
+1

+1这是最清洁和最有效的是O(N)。这正是我想到的算法,你击败了我。 – Keyo 2010-09-09 01:40:46

+0

@ $ data [$ n] [1]为+1服务的是什么?当使用浮点数时,这在我的情况下不起作用。 – 2012-08-27 20:36:06

+3

@Tom,用整数,你希望'[1,2],[3,4]'是'[1,4]'的一个单独范围。在这种情况下,它会读取'if(3> 2 + 1)'然后开始一个新的范围。使用浮点数字,它并不是很有用。取决于您认为小到相同的数字,+1可以被删除或设置为非常小的增量(+ .00001)。 – Matthew 2012-08-30 02:56:35

0

好吧,起草这个,所以它可能有怪癖。用下面看到的数据对它进行测试,似乎工作得很好。可能不是最好的办法,但它是一种方式,它的确行得通。问题让我知道。

function combineRange($array) { 
    if (is_array($array)) { 
     // Sort the array for numerical order 
     sort($array); 

     // Set Defaults 
     $prev = array(); 
     $prev_key = null; 

     foreach ($array as $key => $item) { 
      // First time around setup default data 
      if (empty($prev)) { 
       $prev = $item; 
       $prev_key = $key; 
       continue; 
      } 

      if ($item[0] >= $prev[0] && $item[0] <= $prev[1]) { 
       // Incase the last number was less than do not update 
       if ($array[$prev_key][1] < $item[1]) 
        $array[$prev_key][1] = $item[1]; 

       unset($array[$key]); 
      }else { 
       $prev_key = $key; 
      }  

      $prev = $item; 
     } 
    } 

    return $array; 
} 

$array = array(
    5 => array(13, 16), 
    0 => array(1, 5), 
    1 => array(4, 8), 
    2 => array(19, 24), 
    3 => array(6, 9), 
    4 => array(11, 17), 
    6 => array(21, 30), 
); 

var_dump(combineRange($array)); 

输出:

array(3) { 
    [0]=> 
    array(2) { 
    [0]=> 
    int(1) 
    [1]=> 
    int(9) 
    } 
    [3]=> 
    array(2) { 
    [0]=> 
    int(11) 
    [1]=> 
    int(17) 
    } 
    [5]=> 
    array(2) { 
    [0]=> 
    int(19) 
    [1]=> 
    int(30) 
    } 
} 

希望工程为ya!

编辑

我看到我被一个小时打了= \哦!我仍然发布,因为它是一种不同的方法,因为我可能会选择konforce的方法。

2
$input = array(0 => array(1, 5), 
       1 => array(4, 8), 
       2 => array(19, 24), 
       3 => array(6, 9), 
       4 => array(11, 17), 
      ); 


$tmpArray = array(); 
foreach($input as $rangeSet) { 
    $tmpArray = array_unique(array_merge($tmpArray,range($rangeSet[0],$rangeSet[1]))); 
} 


sort($tmpArray); 

$oldElement = array_shift($tmpArray); 
$newArray = array(array($oldElement)); 
$ni = 0; 
foreach($tmpArray as $newElement) { 
    if ($newElement > $oldElement+1) { 
     $newArray[$ni++][] = $oldElement; 
     $newArray[$ni][] = $newElement; 
    } 
    $oldElement = $newElement; 
} 
$newArray[$ni++][] = $oldElement; 

var_dump($newArray); 
+0

这种方法应该可以工作,但是它会放慢到大范围的抓取。 – Matthew 2010-09-02 20:52:16

+0

最佳答案,作品完美。 – 2016-12-29 07:22:23