PHP range_compact(), l’inversa di range()

In PHP esiste la funzione range() che serve a creare un array di elementi a partire da un singolo range di numeri specificato.

Il problema è che in PHP non esiste la funzione contraria, ovvero che da un array di numeri restituisca, in una qualunque forma, la sua rappresentazione compatta. Nel caso specifico di range() la rappresentazione compatta in input consta solo dei due numeri che rappresentano gli estremi del range.

Grazie alla collaborazione di Fabrizio Catalucci (concept) e Fabio Ambrosanio (debugging) sono lieto di pubblicare questa piccola funzione range_compact() che colma proprio questa mancanza delle librerie standard di PHP.

La funzione range_compact() si avvale della funzione speciale common_prefix() per trovare la parte comune di due stringhe.

/**
 *
 * Restituisce la parte comune di due stringhe partendo da sinistra
 * @param string $s1 - Prima stringa
 * @param string $s2 - Seconda stringa
 * @return string Stringa comune alle due iniziando il confronto da sinistra
 * @example common_prefix("abc", "ab") = "ab"
 */
function common_prefix($s1, $s2) {
	$s1 = strval($s1);
	$s2 = strval($s2);

	$n = min(strlen($s1), strlen($s2));
	$i = 0;

	while ($i < $n && $s1{$i} === $s2{$i}) {
		$i++;
	}

	return substr($s1, 0, $i);
}

/**
 *
 * Rappresenta un array ordinato di interi in insiemi.
 * Gli insiemi sono restituiti sotto forma di stringa con formato interpretato compatto,
 * di comoda comprensione per gli essere umani
 *
 * @param array $v - Array di interi da raggruppare (anche in formato stringa), già ordinato in ordine crescente
 * @param int $offset - Numero di cifre da ignorare a partire dall'inizio dell'array
 * @param int $step - Dimensione del passo con cui raggruppare
 * @param bool $compress - Opzione per rappresentare gli insiemi contigui con notazione compressa a seconda delle cifre diverse tra il primo e l'ultimo elemento di ogni insieme
 * @param int $trunc_left - Rimuove le prime N cifre da sinistra nella rappresentazione finale, per entrambi gli estremi di ogni insieme
 * @param string $seq_sep - Separatore di insiemi non contigui
 * @param string $range_sep - Separatore di insiemi contigui
 * @return string Stringa rappresentante il raggruppamento per insiemi
 *
 * @example $a = array(3, 4, 7, 8, 9, 10, 12);
 *          print_r(range_compact($a));
 *          OUTPUT: 3-4, 7-10, 12
 * @example $a = array(30, 40, 70, 80, 90, 100, 120);
 *          print_r(range_compact($a, 0, 10));
 *          OUTPUT: 30-40, 70-100, 120
 * @example $a = array(3906123456780, 3906123456781, 3906123456782, 3906123456785, 3906123456790, 3906123456791, 3906123456792, 3906123456793, 3906123456794, 3906123456795, 3906123456796, 3906123456797, 3906123456798, 3906123456799, 3906123456800);
 *          print_r(range_compact($a, 0, 1, true, 2));
 *          OUTPUT: 06123456780-2, 06123456785, 06123456790-800
 *          print_r(range_compact($a, 0, 1, false, 2));
 *          OUTPUT: 06123456780-06123456782, 06123456785, 06123456790-06123456800
 *          print_r(range_compact($a, 0, 1));
 *          OUTPUT: 3906123456780-3906123456782, 3906123456785, 3906123456790-3906123456800
 */

function range_compact($v, $offset = 0, $step = 1, $compress = false, $trunc_left = 0, $seq_sep = ", ", $range_sep = "-") {
	$lun = count($v) + 1;
	$firstseq = $offset;
	$lastseq = $firstseq;

	for($i = $offset + 1; $i < $lun; $i++) { 		if($v[$i - 1] == $v[$i] - $step) { 			$lastseq = $i; 		} else { 		$sysc = $sysc . substr($v[$firstseq], $trunc_left); 		if($lastseq > $firstseq) {
			if($compress == false) {
				$sysc = $sysc . $range_sep . substr($v[$lastseq], $trunc_left);
			} else {
				$lastelem = substr($v[$lastseq], strlen(common_prefix($v[$firstseq],$v[$lastseq])));
				$sysc = $sysc . $range_sep . $lastelem;
			}
		}

		if($i < $lun - 1) $sysc = $sysc . $seq_sep; 			$firstseq = $i; 			$lastseq = $firstseq; 		} 	} 	if($lastseq > $firstseq) {
		return $sysc . substr($v[$firstseq], $trunc_left) . $range_sep . substr($v[$lun], $trunc_left);
	} else {
		return $sysc . substr($v[$lun], $trunc_left);
	}
}
comments powered by HyperComments