2015-05-07 158 views
4

我在c#定義的字符串數組作爲從字符串中刪除空值[,]

string[,] options = new string[100,3]; 

在整個它被填充數據但不總是充滿的代碼。

所以,如果我有80個部分填充和20個部分沒有填充。 20個零件中有零個,最後是零個零件。有沒有一種簡單的方法來調整數組,這樣填充之後數組是一樣的

String[,] options = new string[80,3]; 

它必須基於第一組3個空它發現的位置被調整。

如果這是一個交錯數組我會做

options = options.Where(x => x != null).ToArray(); 
+0

因此,如果整行是空的,你刪除它? – xanatos

+0

這就是我正在尋找的。我將如何完成? –

+3

我想你會更好'List >'或者可能是一個自定義類的實例列表,其中3個字符串值爲屬性。 – juharr

回答

7

的方法是相當長的,因爲它要檢查每連續兩次......

public static string[,] RemoveEmptyRows(string[,] strs) 
{ 
    int length1 = strs.GetLength(0); 
    int length2 = strs.GetLength(1); 

    // First we count the non-emtpy rows 
    int nonEmpty = 0; 

    for (int i = 0; i < length1; i++) 
    { 
     for (int j = 0; j < length2; j++) 
     { 
      if (strs[i, j] != null) 
      { 
       nonEmpty++; 
       break; 
      } 
     } 
    } 

    // Then we create an array of the right size 
    string[,] strs2 = new string[nonEmpty, length2]; 

    for (int i1 = 0, i2 = 0; i2 < nonEmpty; i1++) 
    { 
     for (int j = 0; j < length2; j++) 
     { 
      if (strs[i1, j] != null) 
      { 
       // If the i1 row is not empty, we copy it 
       for (int k = 0; k < length2; k++) 
       { 
        strs2[i2, k] = strs[i1, k]; 
       } 

       i2++; 
       break; 
      } 
     } 
    } 

    return strs2; 
} 

使用它像:

string[,] options = new string[100, 3]; 
options[1, 0] = "Foo"; 
options[3, 1] = "Bar"; 
options[90, 2] = "fiz"; 
options = RemoveEmptyRows(options); 

至於建議由阿列克謝,有這樣做的另一種方式:

public static string[,] RemoveEmptyRows2(string[,] strs) 
{ 
    int length1 = strs.GetLength(0); 
    int length2 = strs.GetLength(1); 

    // First we put somewhere a list of the indexes of the non-emtpy rows 
    var nonEmpty = new List<int>(); 

    for (int i = 0; i < length1; i++) 
    { 
     for (int j = 0; j < length2; j++) 
     { 
      if (strs[i, j] != null) 
      { 
       nonEmpty.Add(i); 
       break; 
      } 
     } 
    } 

    // Then we create an array of the right size 
    string[,] strs2 = new string[nonEmpty.Count, length2]; 

    // And we copy the rows from strs to strs2, using the nonEmpty 
    // list of indexes 
    for (int i1 = 0; i1 < nonEmpty.Count; i1++) 
    { 
     int i2 = nonEmpty[i1]; 

     for (int j = 0; j < length2; j++) 
     { 
      strs2[i1, j] = strs[i2, j]; 
     } 
    } 

    return strs2; 
} 

這一個在權衡內存與時間之間選擇時間。它可能更快,因爲它不必檢查每一行兩次,但它使用更多的內存,因爲它放置在非空索引列表的某處。

+0

你打敗了我。但從我所看到的除了遍歷每個部分的數組之外,沒有乾淨的方法來做到這一點。 – CBRRacer

+0

獲取非空行的列表可能會更短... –

+1

@AlexeiLevenkov我可以選擇內存和速度。我選擇儘量減少內存的使用,並儘量減少速度。 – xanatos

0

我去了所有的行,直到你找到一個行的所有空值:

需要一些清理,並顯然將刪除首先空一行後發生的非空行。要求是不是在這裏太清楚

編輯:剛纔見到的評論明確要求刪除所有空行 - 我已經調整了下方,以避免downvotes,但一個更全面的答案已經被接受(和更高效):)

void Main() 
{ 
    string[,] options = new string[100,3]; 

    options[0,0] = "bleb"; 
    options[1,1] = "bleb"; 
    options[2,0] = "bleb"; 
    options[2,1] = "bleb"; 
    options[3,2] = "bleb"; 
    options[4,1] = "bleb"; 

    string[,] trimmed = TrimNullRows(options); 

    Console.WriteLine(trimmed); 
} 

public string[,] TrimNullRows(string[,] options) 
{ 
    IList<string[]> nonNullRows = new List<string[]>(); 
    for (int x = 0; x < options.GetLength(0); x++) 
    { 
     bool allNull = true; 

     var row = new string[options.GetLength(1)]; 

     for (int y = 0; y < options.GetLength(1); y++) 
     { 
      row[y] = options[x,y]; 
      allNull &= options[x,y] == null; 
     } 


     if (!allNull) 
     { 
      nonNullRows.Add(row); 
     } 
    } 

    var optionsTrimmed = new string[nonNullRows.Count, options.GetLength(1)]; 

    for (int i=0;i<nonNullRows.Count;i++) 
    { 
     for (int j=0;j<options.GetLength(1);j++) 
     { 
      optionsTrimmed[i, j] = nonNullRows[i][j]; 
     } 
    } 


    return optionsTrimmed; 
} 
0

另一個使用LINQ

static string[,] RemoveNotNullRow(string[,] o) 
{ 
    var rowLen = o.GetLength(1); 
    var notNullRowIndex = (from oo in o.Cast<string>().Select((x, idx) => new { idx, x }) 
       group oo.x by oo.idx/rowLen into g 
       where g.Any(f => f != null) 
       select g.Key).ToArray(); 

    var res = new string[notNullRowIndex.Length, rowLen]; 

    for (int i = 0; i < notNullRowIndex.Length; i++) 
    { 
     Array.Copy(o, notNullRowIndex[i] * rowLen, res, i * rowLen, rowLen); 
    } 
    return res; 
} 
0

變種還可以讓自己的一些助手鋸齒和多維度之間轉換。當然,這非常愚蠢,但對於像你展示的那樣小的數組(也是非常稀疏的數組),它會很好。

void Main() 
{ 
    string[,] options = new string[100,3]; 

    options[3, 1] = "Hi"; 
    options[5, 0] = "Dan"; 

    var results = 
     options 
      .JagIt() 
      .Where(i => i.Any(j => j != null)) 
      .UnjagIt(); 

    results.Dump(); 
} 

static class Extensions 
{ 
    public static IEnumerable<IEnumerable<T>> JagIt<T>(this T[,] array) 
    { 
     for (var i = 0; i < array.GetLength(0); i++) 
      yield return GetRow(array, i); 
    } 

    public static IEnumerable<T> GetRow<T>(this T[,] array, int rowIndex) 
    { 
     for (var j = 0; j < array.GetLength(1); j++) 
      yield return array[rowIndex, j]; 
    } 

    public static T[,] UnjagIt<T>(this IEnumerable<IEnumerable<T>> jagged) 
    { 
     var rows = jagged.Count(); 
     if (rows == 0) return new T[0, 0]; 

     var columns = jagged.Max(i => i.Count()); 

     var array = new T[rows, columns]; 

     var row = 0; 
     var column = 0; 

     foreach (var r in jagged) 
     { 
      column = 0; 

      foreach (var c in r) 
      { 
      array[row, column++] = c; 
      } 

      row++; 
     } 

     return array; 
    } 
} 

JagIt方法很簡單,當然 - 我們只是遍歷行,yield的個別項目。這給了我們一個枚舉枚舉,我們可以很容易地在LINQ中使用它。如果需要,當然可以將它們轉換爲數組(例如,Select(i => i.ToArray()).ToArray())。

UnjagIt方法更健談一點,因爲我們需要先創建正確尺寸的目標陣列。並沒有unyield指令來簡化:D

這當然是相當低效的,但這不一定是個問題。例如,通過保持內部枚舉爲array,您可以節省一些迭代次數 - 這將使我們不必遍歷所有內部項目。

我主要是把它作爲@xanatos內存密集型,CPU廉價(相對)的內存廉價,CPU密集型替代品。

當然,主要的好處是它可以用來將任何多維數組視爲鋸齒狀數組,並將它們再次轉換回來。一般的解決方案通常不是效率最高的:D