En javascript cómo convertir la secuencia de números en una matriz de rango de números?

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

  • ¿Cómo estás determinar la ubicación de un rango empieza y termina?
  • He editado tu pregunta (eliminado ejemplo de título, el formato del cuerpo). Usted puede motivar a la gente a ayudar al formato de las preguntas correctamente.
InformationsquelleAutor gokul | 2010-02-16

21 Comentarios

  1. 27

    Aquí es un algoritmo que hice hace algún tiempo, originalmente escrita para C#, ahora me portado 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"]
    • sugeriría clasificación de los valores de la primera, así que usted puede tratar con una mezcla de valores como: [1,3,2,6,5,7]
    • Pongo esto en npm npmjs.com/package/get-ranges
  2. 5

    Simplemente divertirse con solución de 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;
      }
    • ++ para el truco bucle while. por cierto, este es equiv. function getRanges(c){for(var b=[],a,d=0;d<c.length;)b.push((a=c[d])+(function(b){for(;++a===c[++d];);return--a===b}(a)?"":"-"+a));return b}; (google compilador de cierre)
  3. 5

    Yo estaba buscando precisamente esto. Yo necesitaba una versión de PHP así portado CMS de la solución. Aquí está, para todo el que se acerque a esta pregunta buscando la misma 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;
    }
  4. 5

    He encontrado esta respuesta útil, pero necesita una versión de 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]))
  5. 3

    Muy buena pregunta: he aquí mi intento:

    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 en JSFiddler

  6. 1

    Si simplemente quieres una cadena que representa un intervalo, entonces usted podría encontrar el punto medio de su secuencia, y que se convierte en su valor medio (10 en el ejemplo). Le gustaría a continuación, tomar el primer elemento de la secuencia, y el elemento que precede inmediatamente a su punto medio, y construir su primera secuencia de la representación. Siga el mismo procedimiento para obtener su último elemento, y el elemento que sigue inmediatamente a su punto medio, y construir su última secuencia de la representación.

    //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

    • No la salida de 1-10, ya que los números 1-10 aparecen en secuencia con ninguna falta?
  7. 1

    Aquí es una versión de 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"
  8. 1

    Aquí está mi opinión sobre esto…

    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;
    }
  9. 1

    En 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();
        }
  10. 1

    Aquí es un puerto de los CMS de código para 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; } # }}}
  11. 0

    Usted podría iterar a través de los números y ver si el siguiente número es 1 más grande que el número actual. Así que tienen un:

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

    donde si array[i+1] == array[i]+1; (donde i es el momento de número)
    luego range.end = array[i+1];. A continuación, se pasan a la siguiente i; Si array[i+1] != array[i]+1; luego range.end = array[i];

    podría almacenar los rangos en un vector< range > ranges;

    la impresión de que sería fácil:

    for(int i = 0; i < ranges.size(); i++) {
        range rng = (range)ranges.at(i);
        printf("[%i-%i]", rng.start, rng.end);
    }
  12. 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;
      } 
    
    }

    Algo como eso.

  13. 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)));
    • print_r ya imprime en la salida estándar a menos que se establece el segundo parámetro opcional de verdad: echo print_r(array(), true); o, simplemente, print_r(array());
  14. 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; 
    } 
    }
  15. 0

    Una adaptación de CMS de javascript de la solución para la Fusión Fría

    No ordenar la lista en primer lugar para que 1,3,2,4,5,8,9,10 (o similar) convierte adecuadamente a 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>
    • Gracias… yo había escrito una similar UDF, pero estaba sufriendo de un error. He actualizado este modo aceptaría una lista o matriz y se añade una subrutina para recortar & quitar no numéricos de los elementos antes de intentar la ordenación numérica. (Si esto no es hecho por el usuario proporcionan valores, un error puede ser lanzada.)
  16. 0

    He aquí lo que puse en Swift. Elimina duplicados y ordena la matriz de la primera, y no importa si se da un vacío de la matriz o una matriz de 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
    }
  17. 0

    Pequeña ES6 módulo para los chicos. Se acepta una función para determinar cuando hay que romper la secuencia (breakDetectorFunc param – por defecto es el simple cosa por entero de la secuencia de entrada).
    AVISO: dado que la entrada es abstracta – no hay auto-clasificación antes de su procesamiento, por lo que si su secuencia no está ordenada a hacerlo antes de llamar a este módulo

    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
    */

    primer argumento es la secuencia de entrada matriz ordenada, el segundo es un indicador booleano controlar el modo de salida: si es verdad – solo elemento (fuera de los intervalos) será devuelto como las matrices de todos modos: [1,7],[9,9],[10,10],[12,20], de lo contrario, solo los artículos devueltos y como aparecen en la matriz de entrada

    para la muestra de entrada

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

    devolverá:

    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]]
  18. 0

    Utilizando ES6, una solución es:

    function display ( vector ) { //assume vector sorted in increasing order
    //display e.g.vector [ 2,4,5,6,9,11,12,13,15 ] as "2;4-6;9;11-13;15"
    const l = vector.length - 1; //last valid index of vector array
    //map [ 2,4,5,6,9,11,12,13,15 ] into array of strings (quote ommitted)
    //--> [ "2;", "4-", "-", "6;", "9;", "11-", "-", "13;", "15;" ]
    vector = vector.map ( ( n, i, v ) => //n is current number at index i of vector v
    i < l && v [ i + 1 ] - n === 1 ? //next number is adjacent ? 
    `${ i > 0 && n - v [ i - 1 ] === 1 ? "" : n }-` :
    `${ n };`
    );
    return vector.join ( "" ).  //concatenate all strings in vector array
    replace ( /-+/g, "-" ). //replace multiple dashes by single dash
    slice ( 0, -1 );        //remove trailing ;
    }

    Si desea añadir más espacios para mejorar la legibilidad, sólo añadir llamadas a string.prototype.replace().

    Si el vector de entrada no está ordenada, puede agregar la siguiente línea a la derecha después de la llave de apertura de la display() función:

    vector.sort ( ( a, b ) => a - b ); //sort vector in place, in increasing order.

    Tenga en cuenta que esto podría ser mejorado para evitar la prueba dos veces por entero adjacentness (adjacenthood? Yo no soy un hablante nativo de inglés;-).

    Y, por supuesto, si usted no quiere una sola cadena como de salida, split con «;».

  19. 0

    Necesitaba una versión de PHP que también admite hacia abajo rangos de (por ejemplo, [10,9,8] se convierte a [10-8]). Así que he modificado DisgruntledGoat la versión que portaron los CMS de la solución. También se encarga de las Cadenas en la entrada correctamente.

    function getRanges($nums)
    {
    $ranges = array();
    for ($i = 0; $i < count($nums); $i++) {
    if (!is_numeric($nums[$i]) || !isset($nums[$i+1]) || !is_numeric($nums[$i+1])) {
    $ranges[] = $nums[$i];
    continue;
    }
    $rStart = $nums[$i];
    $rEnd = $rStart;
    $rDiff = $nums[$i+1] > $nums[$i] ? 1 : -1;
    while (isset($nums[$i+1]) && is_numeric($nums[$i+1]) && $nums[$i+1]-$nums[$i] == $rDiff)
    $rEnd = $nums[++$i];
    $ranges[] = $rStart == $rEnd ? $rStart : $rStart.'-'.$rEnd;
    }
    return $ranges;
    }

    Ejemplo:

    getRanges([2,3,4,5,10,18,19,20,"downwards",10,9,8,7,6,5])
    //Returns [2-5,10,18-20,"downwards",10-5]
  20. 0

    Necesitaba Manuscrito código de hoy para resolver este gran problema, muchos años después de la OP — y decidió probar una versión escrita en un estilo más funcional que el de otras respuestas aquí. Por supuesto, sólo el parámetro y el tipo de devolución de las anotaciones de distinguir este código de estándar ES6 JavaScript.

      function toRanges(values: number[],
    separator = '\u2013'): string[] {
    return values
    .slice()
    .sort((p, q) => p - q)
    .reduce((acc, cur, idx, src) => {
    if ((idx > 0) && ((cur - src[idx - 1]) === 1))
    acc[acc.length - 1][1] = cur;
    else acc.push([cur]);
    return acc;
    }, [])
    .map(range => range.join(separator));
    }

    Nota que slice es necesario porque sort tipo en el lugar y nosotros no podemos cambiar la matriz original.

Dejar respuesta

Please enter your comment!
Please enter your name here