2010-02-16 6 views
20

In javascript come convertire la sequenza di numeri in una matrice in un intervallo di numeri?Come convertire la sequenza di numeri in una matrice in un intervallo di numeri

es. [2,3,4,5,10,18,19,20] a [2-5,10,18-20]

+0

Come si determina dove inizia e termina un intervallo? – Sampson

+5

@gokul: ho modificato la tua domanda (esempio rimosso dal titolo, corpo formattato). Puoi motivare le persone ad aiutare formattando correttamente la tua domanda. –

risposta

1

Se si desidera semplicemente una stringa che rappresenta un intervallo, si troverà il punto centrale della sequenza e questo diventerà il valore medio (10 nell'esempio). Dovresti quindi afferrare il primo elemento nella sequenza e l'elemento che ha immediatamente preceduto il punto medio e creare la rappresentazione della prima sequenza. Seguirai la stessa procedura per ottenere il tuo ultimo oggetto e l'oggetto che segue immediatamente il tuo punto medio e costruisci la tua rappresentazione dell'ultima sequenza.

// Provide initial sequence 
var sequence = [1,2,3,4,5,6,7,8,9,10]; 
// Find midpoint 
var midpoint = Math.ceil(sequence.length/2); 
// Build first sequence from midpoint 
var firstSequence = sequence[0] + "-" + sequence[midpoint-2]; 
// Build second sequence from midpoint 
var lastSequence = sequence[midpoint] + "-" + sequence[sequence.length-1]; 
// Place all new in array 
var newArray = [firstSequence,midpoint,lastSequence]; 

alert(newArray.join(",")); // 1-4,5,6-10 

Demo online: http://jsbin.com/uvahi/edit

+3

L'output non dovrebbe essere 1-10, poiché i numeri 1-10 appaiono in sequenza senza nessuno mancante? –

0

Si potrebbe iterare i numeri e vedere se il numero successivo è 1 grande allora il numero attuale. Quindi avere un:

struct range { 
    int start; 
    int end; 
} range; 

dove se array[i+1] == array[i]+1; (dove i è il numero attualmente osservato) poi range.end = array[i+1];. Quindi procedi al prossimo i; Se poi array[i+1] != array[i]+1;range.end = array[i];

è possibile memorizzare gli intervalli in una stampa vector<range> ranges;

sarebbe stato facile:

for(int i = 0; i < ranges.size(); i++) { 
    range rng = (range)ranges.at(i); 
    printf("[%i-%i]", rng.start, rng.end); 
} 
0
; For all cells of the array 
    ;if current cell = prev cell + 1 -> range continues 
    ;if current cell != prev cell + 1 -> range ended 

int[] x = [2,3,4,5,10,18,19,20] 
string output = '['+x[0] 
bool range = false; --current range 
for (int i = 1; i > x[].length; i++) { 
    if (x[i+1] = [x]+1) { 
    range = true; 
    } else { //not sequential 
    if range = true 
    output = output || '-' 
    else 
    output = output || ',' 
    output.append(x[i]','||x[i+1]) 
    range = false; 
    } 

} 

Qualcosa del genere.

23

Ecco un algoritmo che ho fatto some time ago, originariamente scritto per C#, ora ho portato a JavaScript:

function getRanges(array) { 
    var ranges = [], rstart, rend; 
    for (var i = 0; i < array.length; i++) { 
    rstart = array[i]; 
    rend = rstart; 
    while (array[i + 1] - array[i] == 1) { 
     rend = array[i + 1]; // increment the index if the numbers sequential 
     i++; 
    } 
    ranges.push(rstart == rend ? rstart+'' : rstart + '-' + rend); 
    } 
    return ranges; 
} 

getRanges([2,3,4,5,10,18,19,20]); 
// returns ["2-5", "10", "18-20"] 
getRanges([1,2,3,5,7,9,10,11,12,14 ]); 
// returns ["1-3", "5", "7", "9-12", "14"] 
getRanges([1,2,3,4,5,6,7,8,9,10]) 
// returns ["1-10"] 
+7

suggerirebbe di ordinare prima i valori, quindi puoi gestire valori misti come: [1,3,2,6,5,7] – Tracker1

+1

Lo metto in npm https://www.npmjs.com/package/get- range –

1

Ecco una versione per Perl:

use strict; 
use warnings; 

my @numbers = (0,1,3,3,3,4,4,7,8,9,12, 14, 15, 19, 35, 35, 37, 38, 38, 39); 
@numbers = sort {$a <=> $b} @numbers ; # Make sure array is sorted. 

# Add "infinity" to the end of the array. 
$numbers[1+$#numbers] = undef ; 

my @ranges =() ; # An array where the range strings are stored. 

my $start_number = undef ; 
my $last_number = undef ; 
foreach my $current_number (@numbers) 
{ 
    if (!defined($start_number)) 
    { 
    $start_number = $current_number ; 
    $last_number = $current_number ; 
    } 
    else 
    { 
    if (defined($current_number) && (($last_number + 1) >= $current_number)) 
    { 
     $last_number = $current_number ; 
     next ; 
    } 
    else 
    { 
     if ($start_number == $last_number) 
     { 
     push(@ranges, $start_number) ; 
     } 
     else 
     { 
     push(@ranges, "$start_number-$last_number") ; 
     } 
     $start_number = $current_number ; 
     $last_number = $current_number ; 
    } 
    } 
} 

# Print the results 
print join(", ", @ranges) . "\n" ; 
# Returns "0-1, 3-4, 7-9, 12, 14-15, 19, 35, 37-39" 
+0

Una risposta più breve da PerlMonks: [http://www.perlmonks.org/?node_id=87538](http://www.perlmonks.org/?node_id=87538) –

5

Basta divertirsi con soluzione da CMS:

function getRanges (array) { 
    for (var ranges = [], rend, i = 0; i < array.length;) { 
     ranges.push ((rend = array[i]) + ((function (rstart) { 
     while (++rend === array[++i]); 
     return --rend === rstart; 
     })(rend) ? '' : '-' + rend)); 
    } 
    return ranges; 
    } 
+0

++ per il trucco mentre il ciclo. btw, questo è equiv. 'function getRanges (c) {for (var b = [], a, d = 0; d Orwellophile

0

Ecco il mio prendere su questo ...

function getRanges(input) { 

    //setup the return value 
    var ret = [], ary, first, last; 

    //copy and sort 
    var ary = input.concat([]); 
    ary.sort(function(a,b){ 
    return Number(a) - Number(b); 
    }); 

    //iterate through the array 
    for (var i=0; i<ary.length; i++) { 
    //set the first and last value, to the current iteration 
    first = last = ary[i]; 

    //while within the range, increment 
    while (ary[i+1] == last+1) { 
     last++; 
     i++; 
    } 

    //push the current set into the return value 
    ret.push(first == last ? first : first + "-" + last); 
    } 

    //return the response array. 
    return ret; 
}
5

Stavo solo cercando questa cosa esatta. Avevo bisogno di una versione PHP in modo tale che la soluzione di CMS fosse stata trasferita.Ecco, per chi si ferma a questa domanda cercando la stessa cosa:

function getRanges($nums) 
{ 
    $ranges = array(); 

    for ($i = 0, $len = count($nums); $i < $len; $i++) 
    { 
     $rStart = $nums[$i]; 
     $rEnd = $rStart; 
     while (isset($nums[$i+1]) && $nums[$i+1]-$nums[$i] == 1) 
      $rEnd = $nums[++$i]; 

     $ranges[] = $rStart == $rEnd ? $rStart : $rStart.'-'.$rEnd; 
    } 

    return $ranges; 
} 
5

ho trovato questa risposta utile, ma aveva bisogno di una versione di Python:

def GroupRanges(items): 
    """Yields 2-tuples of (start, end) ranges from a sequence of numbers. 

    Args: 
    items: an iterable of numbers, sorted ascendingly and without duplicates. 

    Yields: 
    2-tuples of (start, end) ranges. start and end will be the same 
    for ranges of 1 number 
    """ 
    myiter = iter(items) 
    start = myiter.next() 
    end = start 
    for num in myiter: 
    if num == end + 1: 
     end = num 
    else: 
     yield (start, end) 
     start = num 
     end = num 
    yield (start, end) 


numbers = [1, 2, 3, 5, 6, 7, 8, 9, 10, 20] 
assert [(1, 3), (5, 10), (20, 20)] == list(GroupRanges(numbers)) 
assert [(1, 1)] == list(GroupRanges([1])) 
assert [(1, 10)] == list(GroupRanges([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])) 
0

PHP

function getRanges($nums) { 
sort($nums); 
$ranges = array(); 

for ($i = 0, $len = count($nums); $i < $len; $i++) 
{ 
    $rStart = $nums[$i]; 
    $rEnd = $rStart; 
    while (isset($nums[$i+1]) && $nums[$i+1]-$nums[$i] == 1) 
     $rEnd = $nums[++$i]; 

    $ranges[] = $rStart == $rEnd ? $rStart : $rStart.'-'.$rEnd; 
} 

return $ranges; 
} 


echo print_r(getRanges(array(2,21,3,4,5,10,18,19,20))); 
echo print_r(getRanges(array(1,2,3,4,5,6,7,8,9,10))); 
+1

print_r stampa già su stdout a meno che non si imposti il ​​secondo parametro opzionale su true: echo print_r (array(), true); o semplicemente print_r (array()); –

0
import java.util.ArrayList; 
import java.util.Arrays; 



public class SequencetoRange { 

    /** 
    * @param args 
    */ 
    public static void main(String[] args) { 
    // TODO Auto-generated method stub 

     int num[] = {1,2,3,63,65,66,67,68,69,70,80,90,91,94,95,4,101,102,75,76,71}; 

     int l = num.length; 
     int i; 
     System.out.print("Given number : "); 
     for (i = 0;i < l;i++){ 
      System.out.print(" " + num[i]); 
     } 
     System.out.println("\n"); 
     Arrays.sort(num); 

     ArrayList newArray = new ArrayList(); 
     newArray = getRanges(num); 
     System.out.print("Range : "); 
     for(int y=0;y<newArray.size();y++) 
     { 
      System.out.print(" " +newArray.get(y)); 
     } 
    } 

    public static ArrayList getRanges(int num[]) 
    { 
     ArrayList ranges = new ArrayList(); 
     int rstart, rend; 
     int lastnum = num[num.length-1]; 
     for (int i = 0; i < num.length-1; i++) 
     {  
      rstart = num[i];  
      rend = rstart;  
      while (num[i + 1] - num[i] == 1) 
      {  
       rend = num[i + 1]; 
       // increment the index if the numbers sequential  
       if(rend>=lastnum) 
       { 
        break; 
       } 
       else 
       { 
        i++; 
       } 
      } 
      if(rstart==rend) 
      { 
       ranges.add(rend); 
      } 
      else 
      { 
       ranges.add(+rstart+"..."+rend); 
      } 
     } 
     return ranges; 
    } 
} 
1

In C#

public string compressNumberRange(string inputSeq) 
    { 
     //Convert String array to long List and removing the duplicates 
     List<long> longList = inputSeq.Split(',').ToList().ConvertAll<long>(s => Convert.ToInt64(s)).Distinct().ToList(); 

     //Sort the array 
     longList.Sort(); 

     StringBuilder builder = new StringBuilder(); 


     for (int itr = 0; itr < longList.Count(); itr++) 
     { 
      long first = longList[itr]; 
      long end = first; 

      while (longList[itr + 1] - longList[itr] == 1) //Seq check 
      { 
       end = longList[itr + 1]; 
       itr++; 
       if (itr == longList.Count() - 1) 
        break; 
      } 
      if (first == end) //not seq 
       builder.Append(first.ToString() + ","); 
      else //seq 
       builder.Append(first.ToString() + "-" + end.ToString() + ","); 
     } 

     return builder.ToString(); 
    } 
0

Ho scritto il mio metodo che dipende da Lo-Dash, ma non ti restituisce una serie di intervalli, ma restituisce semplicemente una serie di gruppi di intervalli.

[1,2,3,4,6,8,10] diventa:

[[1,2,3,4],[6,8,10]] 

http://jsfiddle.net/mberkom/ufVey/

1

Ecco un porto di codice del CMS per BASH:

#!/usr/bin/env bash 
# vim: set ts=3 sts=48 sw=3 cc=76 et fdm=marker: # **** IGNORE ****** 
get_range() { RANGE= # <-- OUTPUT     **** THIS ****** 
    local rstart rend i arr=("[email protected]") # ported from **** JUNK ****** 
    for ((i=0 ; i < $# ; i++)); do # http://stackoverflow.com 
     ((rstart = arr[i]))   # /a/2270987/912236 
     rend=$rstart; while ((arr[i+1] - arr[i] == 1)); do 
     ((rend = arr[++i])); done; ((rstart == rend)) && 
    RANGE+=" $rstart" || RANGE+=" $rstart-$rend"; done; } # }}} 
0

Un adattamento di CMS's javascript solution per Cold Fusion

Si ordina innanzitutto l'elenco in modo che 1,3,2,4,5,8,9,10 (o simile) converte correttamente in 1-5,8-10.

<cfscript> 
    function getRanges(nArr) { 
     arguments.nArr = listToArray(listSort(arguments.nArr,"numeric")); 
     var ranges = []; 
     var rstart = ""; 
     var rend = ""; 
     for (local.i = 1; i <= ArrayLen(arguments.nArr); i++) { 
      rstart = arguments.nArr[i]; 
      rend = rstart; 
      while (i < ArrayLen(arguments.nArr) and (val(arguments.nArr[i + 1]) - val(arguments.nArr[i])) == 1) { 
       rend = val(arguments.nArr[i + 1]); // increment the index if the numbers sequential 
       i++; 
      } 
      ArrayAppend(ranges,rstart == rend ? rstart : rstart & '-' & rend); 
     } 
     return arraytolist(ranges); 
    } 
</cfscript> 
+0

Grazie ... Avevo scritto una UDF simile, ma soffriva di un bug. Ho aggiornato questo modo per accettare una lista o un array e ho aggiunto una subroutine per tagliare e rimuovere elementi non numerici prima di tentare l'ordinamento numerico. (Se questo non viene fatto per i valori forniti dall'utente, può essere generato un errore.) –

1

Bellissima domanda: ecco il mio tentativo:

function ranges(numbers){ 
    var sorted = numbers.sort(function(a,b){return a-b;}); 
    var first = sorted.shift(); 
    return sorted.reduce(function(ranges, num){ 
     if(num - ranges[0][1] <= 1){ 
      ranges[0][1] = num;   
     } else { 
      ranges.unshift([num,num]); 
     } 
     return ranges; 
    },[[first,first]]).map(function(ranges){ 
     return ranges[0] === ranges[1] ? 
      ranges[0].toString() : ranges.join('-'); 
    }).reverse(); 
} 

Demo on JSFiddler

0

Ecco quello che ho messo insieme in Swift. Elimina i duplicati e ordina l'array per primo, e non gli importa se ha un array vuoto o un array di uno.

func intArrayToString(array: [Int]) -> String { 
    var intArray = Array(Set(array)) 
    intArray.sortInPlace() 
    if intArray.count == 0 { 
     return "" 
    } 
    var intString = "\(intArray[0])" 
    if intArray.count > 1 { 
     for j in 1..<intArray.count-1 { 
      if intArray[j] == intArray[j-1]+1 { 
       if intArray[j] != intArray[j+1]-1 { 
        intString += "-\(intArray[j])" 
       } 
      } else { 
       intString += ",\(intArray[j])" 
      } 
     } 
     if intArray.last! == intArray[intArray.count-2]+1 { 
      intString += "-\(intArray.last!)" 
     } else { 
      intString += ",\(intArray.last!)" 
     } 
    } 
    return intString 
} 
0

Piccolo modulo ES6 per voi ragazzi. Accetta una funzione per determinare quando è necessario interrompere la sequenza (breakDetectorFunc param - default è la cosa più semplice per l'input della sequenza intera). AVVISO: dal momento di ingresso è astratta - non c'è auto-selezione prima della lavorazione, quindi se la sequenza non è ordinato - serie non è prima di chiamare questo modulo

function defaultIntDetector(a, b){ 
    return Math.abs(b - a) > 1; 
} 

/** 
* @param {Array} valuesArray 
* @param {Boolean} [allArraysResult=false] if true - [1,2,3,7] will return [[1,3], [7,7]]. Otherwise [[1.3], 7] 
* @param {SequenceToIntervalsBreakDetector} [breakDetectorFunc] must return true if value1 and value2 can't be in one sequence (if we need a gap here) 
* @return {Array} 
*/ 
const sequenceToIntervals = function (valuesArray, allArraysResult, breakDetectorFunc) { 
    if (!breakDetectorFunc){ 
     breakDetectorFunc = defaultIntDetector; 
    } 
    if (typeof(allArraysResult) === 'undefined'){ 
     allArraysResult = false; 
    } 

    const intervals = []; 
    let from = 0, to; 
    if (valuesArray instanceof Array) { 
     const cnt = valuesArray.length; 
     for (let i = 0; i < cnt; i++) { 
      to = i; 
      if (i < cnt - 1) { // i is not last (to compare to next) 
       if (breakDetectorFunc(valuesArray[i], valuesArray[i + 1])) { 
        // break 
        appendLastResult(); 
       } 
      } 
     } 
     appendLastResult(); 
    } else { 
     throw new Error("input is not an Array"); 
    } 

    function appendLastResult(){ 
     if (isFinite(from) && isFinite(to)) { 
      const vFrom = valuesArray[from]; 
      const vTo = valuesArray[to]; 

      if (from === to) { 
       intervals.push(
        allArraysResult 
         ? [vFrom, vTo] // same values array item 
         : vFrom // just a value, no array 
       ); 
      } else if (Math.abs(from - to) === 1) { // sibling items 
       if (allArraysResult) { 
        intervals.push([vFrom, vFrom]); 
        intervals.push([vTo, vTo]); 
       } else { 
        intervals.push(vFrom, vTo); 
       } 
      } else { 
       intervals.push([vFrom, vTo]); // true interval 
      } 
      from = to + 1; 
     } 
    } 

    return intervals; 
}; 

module.exports = sequenceToIntervals; 

/** @callback SequenceToIntervalsBreakDetector 
@param value1 
@param value2 
@return bool 
*/ 

primo argomento è la sequenza di ingresso ordinato, secondo è un flag booleano che controlla la modalità di output: se true - un singolo elemento (al di fuori degli intervalli) verrà comunque restituito come array: [1,7], [9,9], [10,10], [12,20], altrimenti singole voci restituiti come appaiono nella matrice di ingresso

per il vostro input di esempio

[2,3,4,5,10,18,19,20] 

restituirà:

sequenceToIntervals([2,3,4,5,10,18,19,20], true) // [[2,5], [10,10], [18,20]] 
sequenceToIntervals([2,3,4,5,10,18,19,20], false) // [[2,5], 10, [18,20]] 
sequenceToIntervals([2,3,4,5,10,18,19,20]) // [[2,5], 10, [18,20]]