1
\$\begingroup\$

I'd like to know what's the most efficient and correct way to do the following:

  1. We're given a list of objects. Here's an exampler object:

    public class Search
    {
        public string ColumnName {get; set;}
        public string Value {get; set;}
    }
    
  2. One of the object's properties can hold only two "types" of string values: A dot-seperated value, and a non-dot-seperated value. For example:

    List<Search> searches = new List<Search> 
    {
        new Search(){ColumnName = "field1", Value = "1"},
        new Search(){ColumnName = "field2", Value = "2"},
        new Search(){ColumnName = "joined1.field3", Value = "2"},
        new Search(){ColumnName = "joined2.field4", Value = "3"},
        new Search(){ColumnName = "joined1.field2", Value = "4"},
    };
    
  3. We need to split the given list by checking the potentially-dot-seperated property value, so that those that are non-dot-seperated are put into a list, and those that are dot-seperated are put into a grouping by the property value.

    The above list should be split into the following structure (Please run the following snippet to display the example):

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
      <head>
        <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
        <meta name="Generator" content="LINQ to XML, baby!" />
      <style type='text/css'>
    body
    {
    	margin: 0.3em 0.3em 0.4em 0.5em;
    	font-family: Verdana;
    	font-size: 80%;
    	background: white;
    }
    
    p, pre
    {
    	margin:0;
    	padding:0;
    	font-family: Verdana;
    }
    
    table
    {
    	border-collapse: collapse;
    	border: 2px solid #17b;
    	border-top: 1px;
    	margin: 0.3em 0.2em;
    }
    
    table.limit
    {
    	border-collapse: collapse;
    	border-bottom: 2px solid #c31;
    }
    
    td, th
    {
    	vertical-align: top;
    	border: 1px solid #aaa;
    	padding: 0.1em 0.2em;
    	margin: 0;
    }
    
    th
    {
    	text-align: left;
    	background-color: #ddd;
    	border: 1px solid #777;
    	font-family: tahoma;
    	font-size:90%;
    	font-weight: bold;
    }
    
    th.member
    {
    	padding: 0.1em 0.2em 0.1em 0.2em;
    }
    
    td.typeheader
    {
    	font-family: tahoma;
    	font-size: 90%;
    	font-weight: bold;
    	background-color: #17b;
    	color: white;
    	padding: 0 0.2em 0.1em 0.1em;
    }
    
    td.n { text-align: right }
    
    a:link.typeheader, a:visited.typeheader
    {
    	font-family: tahoma;
    	font-size: 90%;
    	font-weight: bold;
    	text-decoration: none;
    	background-color: #17b;
    	color: white;
    	float:left;
    }
    
    span.typeglyph
    {
    	font-family: webdings;
    	padding: 0 0.2em 0 0;
    	margin: 0;
    }
    
    table.group
    {
    	border: none;
    	margin: 0;
    }
    
    td.group
    {
    	border: none;
    	padding: 0 0.1em;
    }
    
    div.spacer
    {
    	margin: 0.6em 0;
    }
    
    table.headingpresenter
    {
    	border: none;
    	border-left: 3px dotted #1a5;
    	margin: 1em 0em 1.2em 0.15em;
    }
    
    th.headingpresenter
    {
    	font-family: Arial;
    	border: none;
    	padding: 0 0 0.2em 0.5em;
    	background-color: white;
    	color: green;
    	font-size: 110%;        
    }
    
    td.headingpresenter
    {
    	border: none;
    	padding: 0 0 0 0.6em;
    }
    
    td.summary
    { 
    	background-color: #def;
    	color: #024;
    	font-family: Tahoma;
    	padding: 0 0.1em 0.1em 0.1em;
    }
    
    td.columntotal
    {
    	font-family: Tahoma;
    	background-color: #eee;
    	font-weight: bold;
    	color: #17b;
    	font-size:90%;
    	text-align:right;
    }
    
    span.graphbar
    {
    	background: #17b;
    	color: #17b;
    	margin-left: -2px;
    	margin-right: -2px;
    }
    
    a:link.graphcolumn, a:visited.graphcolumn
    {
    	color: #17b;
    	text-decoration: none;
    	font-weight: bold;
    	font-family: Arial;
    	font-size: 110%;
    	letter-spacing: -0.4em;	
    	margin-left: 0.3em;
    }
    
    i { color: green; }
    
    em { color: red; }
    
    span.highlight { background: #ff8; }
      </style>
    
      <script language='JavaScript' type='text/javascript'>
    
          function toggle(id)
          {
            table = document.getElementById(id);
            if (table == null) return false;
            updown = document.getElementById(id + 'ud');
            if (updown == null) return false;
            expand = updown.innerText == '6';
            updown.innerText = expand ? '5' : '6';
            table.style.borderBottom = expand ? '2px solid' : 'dashed 2px';
            elements = table.rows;
            if (elements.length == 0 || elements.length == 1) return false;
            for (i = 1; i != elements.length; i++)
              if (elements[i].id.substring(0,3) != 'sum')
                elements[i].style.display = expand ? 'block' : 'none';
            return false;
          }
        
      </script>
    </head>
    <body>
    <div>
      <div class="spacer">
        <table id="t2">
          <tr>
            <td class="typeheader" colspan="2">
              <a href="" class="typeheader" onclick="return toggle('t2');">
                <span class="typeglyph" id="t2ud">5</span>List&lt;Search&gt; (2 items)</a>
              <a href="" class="typeheader" style="float:right; padding-left:2pt; margin-left:4pt" onclick="return window.external.CustomClick('1',false);">
                <span style="font-family: webdings; margin-top:1.2pt" id="t2ud">4</span>
              </a>
            </td>
          </tr>
          <tr>
            <th title="System.String">ColumnName</th>
            <th title="System.String">Value</th>
          </tr>
          <tr>
            <td>field1</td>
            <td>1</td>
          </tr>
          <tr>
            <td>field2</td>
            <td>2</td>
          </tr>
        </table>
      </div>
    </div>
    <div>
      <div class="spacer">
        <table id="t6">
          <tr>
            <td class="typeheader" colspan="1">
              <a href="" class="typeheader" onclick="return toggle('t6');">
                <span class="typeglyph" id="t6ud">5</span>List&lt;IGrouping&lt;String,Search&gt;&gt; (2 items)</a>
              <a href="" class="typeheader" style="float:right; padding-left:2pt; margin-left:4pt" onclick="return window.external.CustomClick('5',false);">
                <span style="font-family: webdings; margin-top:1.2pt" id="t6ud">4</span>
              </a>
            </td>
          </tr>
          <tr>
            <td>
              <table class="group">
                <tr>
                  <td class="group">
                    <i>Key=</i>
                  </td>
                  <td class="group">joined1</td>
                </tr>
              </table>
              <table id="t7">
                <tr>
                  <td class="typeheader" colspan="2">
                    <a href="" class="typeheader" onclick="return toggle('t7');">
                      <span class="typeglyph" id="t7ud">5</span>IGrouping&lt;String,Search&gt; (2 items)</a>
                    <a href="" class="typeheader" style="float:right; padding-left:2pt; margin-left:4pt" onclick="return window.external.CustomClick('6',false);">
                      <span style="font-family: webdings; margin-top:1.2pt" id="t7ud">4</span>
                    </a>
                  </td>
                </tr>
                <tr>
                  <th title="System.String">ColumnName</th>
                  <th title="System.String">Value</th>
                </tr>
                <tr>
                  <td>joined1.field3</td>
                  <td>2</td>
                </tr>
                <tr>
                  <td>joined1.field2</td>
                  <td>4</td>
                </tr>
              </table>
            </td>
          </tr>
          <tr>
            <td>
              <table class="group">
                <tr>
                  <td class="group">
                    <i>Key=</i>
                  </td>
                  <td class="group">joined2</td>
                </tr>
              </table>
              <table id="t8">
                <tr>
                  <td class="typeheader" colspan="2">
                    <a href="" class="typeheader" onclick="return toggle('t8');">
                      <span class="typeglyph" id="t8ud">5</span>IGrouping&lt;String,Search&gt; (1 item)</a>
                    <a href="" class="typeheader" style="float:right; padding-left:2pt; margin-left:4pt" onclick="return window.external.CustomClick('7',false);">
                      <span style="font-family: webdings; margin-top:1.2pt" id="t8ud">4</span>
                    </a>
                  </td>
                </tr>
                <tr>
                  <th title="System.String">ColumnName</th>
                  <th title="System.String">Value</th>
                </tr>
                <tr>
                  <td>joined2.field4</td>
                  <td>3</td>
                </tr>
              </table>
            </td>
          </tr>
        </table>
      </div>
    </div>
    </body>
    </html>

  4. What I currently have:

    var dotSeperatedOrNotGroups = searches.GroupBy(s => s.ColumnName.Contains('.')).ToList();
    var nonDotSeperatedList = dotSeperatedOrNotGroups
                               .Where(s => s.Key == false)
                               .SelectMany(s=>s)
                               .ToList();
    var dotSeperatedGroups = dotSeperatedOrNotGroups
                               .Where(s => s.Key == true)
                               .SelectMany(g=>g)
                               .GroupBy(g => g.ColumnName.Split('.')[0])
                               .ToList();
    

    How can it be improved?

\$\endgroup\$
0
\$\begingroup\$

If two categories is all you need then it's easier to use ToLookup instead of GroupBy. With it you'll be able to select the group by a bool value:

var nonDotSeperatedList = dotSeperatedOrNotGroups[false]...
var dotSeperatedGroups = dotSeperatedOrNotGroups[true]...

You could also use a Dictionar<bool, T> but an ILookup<T> has the advantage that its indexer will return an empty collection instead of throwing an exception if a key does not exist.

Now you can also rename the dotSeperatedOrNotGroups to dotSeperatedGroups so the usage is even easier to read with the bools

var nonDotSeperatedList = dotSeperatedGroups[false]...
var dotSeperatedGroups = dotSeperatedGroups[true]...

But remember to not call ToList anymore so that you can work with ILookup<T> and not a list:

var dotSeperatedGroups = searches.ToLookup(s => s.ColumnName.Contains('.'));

In your example calling ToList is unnecessary too. The group is already created and calling ToList just enumerates it again.

\$\endgroup\$
  • \$\begingroup\$ Thanks. I can see how I can do it with ILookup<bool, Search>, but I'm not sure, how can I use it with ILookup<T> (T, I guess, being Search)? \$\endgroup\$ – HelterSkelter Aug 31 '17 at 22:46
1
\$\begingroup\$

I would bring dot into the class. An have ctor for cleaner new.

public class Search
{
    public string ColumnName {get; set;}
    public bool? ColumnNameDot 
    {
       if(String.InullOrEmpty) 
       { 
           return null;
       }
       else 
       {
           return ColumnName.Contains('.');
       }
    }
    public string Value {get; set;}
    public Search (string columnName, string value)
    {
        ColumnName = columnName;
        Value = value;
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.