views:

103

answers:

1

Judging from the result of my last inquiry, I need to calculate and preset the widths of a set of columns in a table that is being made into an Excel file. Unfortunately, the string data is stored in a row-based format, but the widths must be calculated in a column-based format. The data for the spreadsheets are generated from the following two collections:

var dictFiles = l.Items.Cast<SPListItem>().GroupBy(foo => foo.GetSafeSPValue("Category")).ToDictionary(bar => bar.Key);
StringDictionary dictCols = GetColumnsForItem(l.Title);

Where l is an SPList whose title determines which columns are used. Each SPListItem corresponds to a row of data, which are sorted into separate worksheets based on Category (hence the dictionary). The second line is just a simple StringDictionary that has the column name (A, B, C, etc.) as a key and the corresponding SPListItme field display name as the corresponding value. So for each Category, I enumerate through dictFiles[somekey] to get all the rows in that sheet, and get the particular cell data using SPListItem.Fields[dictCols[colName]].

What I am asking is, is there a quick or concise method, for any one dictFiles[somekey], to retrieve a readout of the longest string in each column provided by dictCols? If it is impossible to get both quickness and conciseness, I can settle for either (since I always have the O(n*m) route of just enumerating the collection and updating an array whenever strCurrent.Length > strLongest.Length). For example, suppose I had a set of 3 items, and dictCols specified the fields Field1, Field2, and Field3. The goal table might look like the following:

Item#  Field1     Field2     Field3
1      Oarfish    Atmosphere Pretty
2      Raven      Radiation  Adorable
3      Sunflower  Flowers    Cute

I'd like a function which could cleanly take the collection of items 1, 2, and 3 and output in the correct order...

Sunflower, Atmosphere, Adorable

Using .NET 3.5 and C# 3.0.

+1  A: 

Unfortunately, searching for an item with the highest value for a specified observable on a set of n items means that each item must be checked. So the complexity is O(n). This stands if no assumtion is made on the collection's order.

Having m collections to scan, the complexity (as you already figured out) is O(m x n).


EDIT [Erik Burigo]: This part of answer has been removed because it did not respond to the question's needs. [omissis]


After having misunderstood the question I finally catched the point. I can't see a more compact and elegant syntax than the one I'm proposing below.

  var collection =
      new List<Dictionary<String, String>>
      {
          new Dictionary<string, string> {{"Field1", "Oarfish"}, {"Field2", "Atmosphere"}, {"Field3", "Pretty"}},
          new Dictionary<string, string> {{"Field1", "Raven"}, {"Field2", "Radiation"}, {"Field3", "Adorable"}},
          new Dictionary<string, string> {{"Field1", "Sunflower"}, {"Field2", "Flowers"}, {"Field3", "Cute"}}
     };

 var fields = new[] {"Field1", "Field2", "Field3"};

 var maximums = new List<String>(fields.Length);
 foreach (var field in fields)
 {
      maximums.Add(Field(collection, field).OrderByDescending(fieldItem => fieldItem.Length).First());
 }

where

  static IEnumerable<String> Field(IEnumerable<Dictionary<String, String>> collection, String field)
  {
      foreach (var row in collection)
      {
          yield return row[field];
      }
  }

Thus relaying on an accumulator list. This solution needs the number of fields of the various rows not to be varying from row to row (but it seems to be the case).

However, using an accumulator and an invoked method is not truly compact. What you really need is a steamlined way to transpose your data structure in order to figure the longest string for each field. As far as I know there is no shortcut in the framework to do this, thus the resulting method (Field(...)) will be tailored on your specific data structure (a collection of string-indexed strings).

Being so, the Field(...) method could be further enhanced by making it provide the longest string, thus shortening the overall invocation statement. So, the more work we put in that specific method, the more the solution approaches what you already had in mind before posting the question.

EDIT [Erik Burigo]: Changed in order to make the collection more similar to the one posted in the question.

Erik Burigo
Alright, so O(m x n) is my best speed. Is there a cleaner. more concise search method than the ludicrously bulky double-looping "Check if strCurrent.Length > strLongest.Length" double loop? Because that will be ludicrously bulky.
ccomet
Your current method retrieves the longest string in each row, not each column. I get an output of `Atmosphere, Radiation, Sunflower` from your code, which is incorrect. It also sorts by the longest string, I need to retain the sort order of the columns specified by dictCols.
ccomet
Okay. Now I got it. I apologize for the misunderstanding. I'm going to edit the answer and thinking about the correct solution.
Erik Burigo
Alright. This works for an array of arrays. I'll have to adapt it to work with a collection of class objects containing a collection property referenced by a string index, but it is a good way to start.
ccomet
It may have been additional work to come up with the solution (and yours looks a lot cleaner than the mess I built from the previous version), but the fact is it is cleaner. I'll update my code with your fixes. Even with the method invoked, I still am able to "input collection of fields, get array" instead of "build a proper sized array and update the values as I iterate each one slowly".
ccomet