2012-08-29 8 views
5

Voglio creare un oggetto Excel.Range da un elenco di riferimenti di cella generati dinamicamente.Semplifica un elenco di celle excel per creare un intervallo

Excel.Range outputRange = sheet.get_Range(strCellRange, Type.Missing); 

Poiché strCellRange può diventare piuttosto grande, fornisce eccezioni com. Quindi voglio semplificare per avere notazione di intervallo con unione.

ad es.

string strCellRange = "F2,G2,H2,I2,J2,K2,L2,F7,G7,H7,I7,J7,K7,L7,F12,G12,H12,I12,J12,K12,L12,F17,G17,H17,I17,J17,K17,L17,F22,G22,H22,I22,J22,K22,L22,F27,G27,H27,I27,J27,K27,L27"; 

a

string strCellRange = "F2:L2,F7:L7,F12:L12,F17:L17,F22:L22,F27:L27"; 
  1. Esiste un metodo di Excel per creare un oggetto Range con molti riferimenti di cella?
  2. Esiste un algoritmo noto per ottenere una semplificazione superiore (un algoritmo a matrice)?
+0

In VBA di Excel si usa 'Union' per fare ciò 1 e 2. Non riesco a capire come - o se - puoi farlo tramite 'C#' – brettdj

+0

@brettdj: Sì, potrei usare application.Union (, ). Ma AFAIK nella mia stringa originale la virgola "," è anche l'operatore del sindacato. –

+0

Vedere il mio aggiornamento qui sotto. rgds – brettdj

risposta

3

Gayan,

In VBA si potrebbe forzare la stringa direttamente a un intervallo con

Sub Test() 
Dim rng1 As Range 
Dim strCellRange As String 
strCellRange = "F2,G2,H2,I2,J2,K2,L2,F7,G7,H7,I7,J7,K7,L7,F12,G12,H12,I12,J12,K12,L12,F17,G17,H17,I17,J17,K17,L17,F22,G22,H22,I22,J22,K22,L22,F27,G27,H27,I27,J27,K27,L27" 
Set rng1 = Range(strCellRange) 
Set rng1 = Union(rng1, rng1) 
Debug.Print rng1.Address 
End Sub 
+0

+1. Bello e semplice – shahkalpesh

+0

OK, ma il problema dell'OP è il fatto che la generazione della Range data la stringa genera un'eccezione a causa del numero di celle nella stringa ... –

+0

@brettdj: Hai risposto al mio problema di semplificazione, ora posso prendere me stesso unione quando si avvicina il limite delle cellule. In definitiva, potrei anche raggiungere un altro limite con la versione semplificata, ma poi posso rompere e avere intervalli diversi. –

0

Non penso che esista un metodo per recuperare un lungo intervallo di riferimenti di cella. Preferirei prendere il foglio completo e poi navigare con esso attraverso le codifiche.

1

VBA

Function Unionize(src As Range) As Range 
Dim cell As Range 
Dim unionizedRange As Range 

For Each cell In src 
    If unionizedRange Is Nothing Then 
     Set unionizedRange = cell 
    Else 
     Set unionizedRange = Union(unionizedRange, cell) 
    End If 
Next 

Set Unionize = unionizedRange 
End Function 

C# (taglio grezzo, non eseguirlo tramite compilatore per la sintassi)

Excel.Range Unionize(Excel.Range src) 
{ 
    Excel.Range unionizedRange; 

    foreach (Excel.Range cell in src) 
    { 
     if (unionizedRange == null) 
     { 
      unionizedRange = cell; 
     } 
     Else 
     { 
      unionizedRange = Application.Union(unionizedRange, cell); 
     } 
    } 
    return unionizedRange; 
} 

EDIT: Sulla base di @ brettdj soluzione

Excel.Range outputRange = sheet.get_Range(strCellRange, Type.Missing); 
strCellRange = Application.Union(outputRange, outputRange).Address(false, false); 
+0

Da quello che ho capito, la versione abbreviata di strCellRange non è l'obiettivo dell'OP. È solo un modo per aggirare il fatto che il tentativo di ottenere il Range dalla stringa originale genera un'eccezione –

2

Questo potrebbe essere un punto di partenza (non funziona per le colonne al di là di Z, e non si identifica rettangoli):

private string CompactRangeStringByRows(string strCellRange) 
    { 
     SortedDictionary<int, SortedList<char, char>> rows = new SortedDictionary<int, SortedList<char, char>>(); 
     foreach (string aCell in strCellRange.Split(new Char[] { ',' })) 
     { 
      char col = aCell[0]; 
      int row = int.Parse(aCell.Substring(1, aCell.Length - 1)); 
      SortedList<char, char> cols; 
      if (!rows.TryGetValue(row, out cols)) 
      { 
       cols = new SortedList<char, char>(); 
       rows[row] = cols; 
      } 
      cols.Add(col, col); 
     } 
     StringBuilder sb = new StringBuilder(); 
     bool first = true; 
     foreach (KeyValuePair<int, SortedList<char, char>> rowCols in rows) 
     { 
      char minCol = '0'; 
      char maxCol = '0'; 
      foreach (char col in rowCols.Value.Keys) 
      { 
       if (minCol == '0') 
       { 
        minCol = col; 
        maxCol = col; 
       } 
       else 
       { 
        if (col == maxCol + 1) 
         maxCol = col; 
        else 
        { 
         AddRangeString(sb, first, rowCols.Key, minCol, maxCol); 
         minCol = col; 
         maxCol = col; 
         first = false; 
        } 
       } 
      } 
      AddRangeString(sb, first, rowCols.Key, minCol, maxCol); 
      first = false; 
     } 
     return sb.ToString(); 
    } 

    private void AddRangeString(StringBuilder sb, bool first, int row, char minCol, char maxCol) 
    { 
     if (!first) 
      sb.Append(','); 
     sb.Append(minCol); 
     sb.Append(row); 
     if (maxCol != minCol) 
     { 
      sb.Append(':'); 
      sb.Append(maxCol); 
      sb.Append(row); 
     } 
    } 
+0

Una volta completamente sviluppato questo sarà il migliore risposta. Molto utile +1. Posso generare i riferimenti di cella con stile R1C1. o anche (row^col) che renderà l'algoritmo molto utile. Lo proveremo sicuramente quando il tempo lo permetterà. Ma per ora penso che la soluzione di brettdj sia ok. –

+0

Sviluppato un algoritmo simile a "manualmente" per ottenere linee e rettangoli nell'intervallo. Vedi la mia risposta qui sotto. –

1

prima avere alcune classi tenendo riferimenti ...

public class CellRef : IEquatable<CellRef> 
{ 
    public int Row { get; private set; } 
    public int Col { get; private set; } 

    // some more code... 
} 

public class CellRange : IEquatable<CellRange> 
{ 
    public CellRef Start { get; private set; } 
    public CellRef Stop { get; private set; } 

    // some more code... 
} 

Quindi l'algoritmo e il metodo ... L'elenco di celle deve essere inserito in un elenco e ordinato prima di passare a questo metodo.

public static string GetSimplifiedRangeString(List<CellRef> cellList) 
    { 
     #region Column wise simplify (identify lines) 

     Dictionary<CellRef, CellRef> rowRanges = new Dictionary<CellRef, CellRef>(new CellRefEqualityComparer()); 

     int currentRangeStart = 0; 
     for (int currentRangeStop = 0; currentRangeStop < cellList.Count; currentRangeStop++) 
     { 
      CellRef currentCell = cellList[currentRangeStop]; 
      CellRef previousCell = (currentRangeStop == 0) ? null : cellList[currentRangeStop - 1]; 

      bool cont = IsContigousX(currentCell, previousCell); 

      if (!cont) 
      { 
       currentRangeStart = currentRangeStop; 
      } 

      if (!rowRanges.ContainsKey(cellList[currentRangeStart])) 
       rowRanges.Add(cellList[currentRangeStart], cellList[currentRangeStop]); 
      else 
       rowRanges[cellList[currentRangeStart]] = cellList[currentRangeStop]; 
     } 

     #endregion 


     #region Row wise simplify (identify rectangles) 

     List<CellRange> rangeList = new List<CellRange>(); 
     foreach (KeyValuePair<CellRef, CellRef> range in rowRanges) 
     { 
      rangeList.Add(new CellRange(range.Key, range.Value));     
     }    
     Dictionary<CellRange, CellRange> colRanges = new Dictionary<CellRange, CellRange>(new CellRangeEqualityComparer()); 

     currentRangeStart = 0; 
     for (int currentRangeStop = 0; currentRangeStop < rangeList.Count; currentRangeStop++) 
     { 
      CellRange currentCellRange = rangeList[currentRangeStop]; 
      CellRange previousCellRange = (currentRangeStop == 0) ? null : rangeList[currentRangeStop - 1]; 

      bool cont = IsContigousY(currentCellRange, previousCellRange); 

      if (!cont) 
      { 
       currentRangeStart = currentRangeStop; 
      } 

      if (!colRanges.ContainsKey(rangeList[currentRangeStart])) 
       colRanges.Add(rangeList[currentRangeStart], rangeList[currentRangeStop]); 
      else 
       colRanges[rangeList[currentRangeStart]] = rangeList[currentRangeStop]; 
     } 

     #endregion 


     #region Simplify ranges (identify atomic lines and rectangles) 

     StringBuilder retStr = new StringBuilder(); 
     foreach (KeyValuePair<CellRange, CellRange> ranges in colRanges) 
     { 
      string rangePart = string.Empty; 
      if (ranges.Key.Equals(ranges.Value)) 
      { 
       if (ranges.Key.Start.Equals(ranges.Key.Stop)) 
       { 
        rangePart = ranges.Key.Start.ToString(); 
       } 
       else 
       { 
        rangePart = ranges.Key.ToString(); 
       } 
      } 
      else 
      { 
       rangePart = new CellRange(ranges.Key.Start, ranges.Value.Stop).ToString(); 
      } 

      if (retStr.Length == 0) 
      { 
       retStr.Append(rangePart); 
      } 
      else 
      { 
       retStr.Append("," + rangePart); 
      } 
     } 

     return retStr.ToString(); 

     #endregion 
    } 

    /// <summary> 
    /// Checks whether the given two cells represent a line. 
    /// </summary> 
    /// <param name="currentCell">Line start</param> 
    /// <param name="previousCell">Line end</param> 
    /// <returns></returns> 
    private static bool IsContigousX(CellRef currentCell, CellRef previousCell) 
    { 
     if (previousCell == null) 
      return false; 
     return (currentCell.Row == previousCell.Row) && (currentCell.Col == (previousCell.Col + 1)); 
    } 

    /// <summary> 
    /// Checks whether the given two cells represents a rectangle. 
    /// </summary> 
    /// <param name="currentCellRange">Top-left cell</param> 
    /// <param name="previousCellRange">Bottom-right cell</param> 
    /// <returns></returns> 
    private static bool IsContigousY(CellRange currentCellRange, CellRange previousCellRange) 
    { 
     if (previousCellRange == null) 
      return false; 

     bool sameVertically = (currentCellRange.Start.Col == previousCellRange.Start.Col) && (currentCellRange.Stop.Col == previousCellRange.Stop.Col); 
     bool contigous = (currentCellRange.Start.Row == currentCellRange.Stop.Row) && (previousCellRange.Start.Row == previousCellRange.Stop.Row) && ((previousCellRange.Stop.Row + 1) == currentCellRange.Stop.Row); 
     return sameVertically && contigous; 
    } 

Spero che questo aiuti qualcuno.