title: How to make a C# 'grep' more Functional using LINQ?
body: <p>I have a method that performs a simplistic 'grep' across files, using an enumerable of "search strings".  (Effectively, I'm doing a very naive "Find All References") </p>
  
  <pre><code>IEnumerable&lt;string&gt; searchStrings = GetSearchStrings();
  IEnumerable&lt;string&gt; filesToLookIn = GetFiles();
  MultiMap&lt;string, string&gt; references = new MultiMap&lt;string, string&gt;();
  
  foreach( string fileName in filesToLookIn )
  {
      foreach( string line in File.ReadAllLines( fileName ) )
      {
          foreach( string searchString in searchStrings )
          {
              if( line.Contains( searchString ) )
              {
                  references.AddIfNew( searchString, fileName );
              }
          }
      }
  }
  </code></pre>
  
  <p>Note: <code>MultiMap&lt;TKey,TValue&gt;</code> is roughly the same as <code>Dictionary&lt;TKey,List&lt;TValue&gt;&gt;</code>, just avoiding the NullReferenceExceptions you'd normally encounter.</p>
  
  <hr/>
  
  <p>I have been trying to put this into a more "functional" style, using chained LINQ extension methods but haven't figured it out.</p>
  
  <p>One dead-end attempt:</p>
  
  <pre><code>// I get lost on how to do a loop within a loop here...
  // plus, I lose track of the file name
  var lines = filesToLookIn.Select( f =&gt; File.ReadAllLines( f ) ).Where( // ???
  </code></pre>
  
  <p>And another (hopefully preserving the file name this time):</p>
  
  <pre><code>var filesWithLines =
      filesToLookIn
          .Select(f =&gt; new { FileName = f, Lines = File.ReadAllLines(f) });
  
  var matchingSearchStrings =
      searchStrings
          .Where(ss =&gt; filesWithLines.Any(
                           fwl =&gt; fwl.Lines.Any(l =&gt; l.Contains(ss))));
  </code></pre>
  
  <p>But I still seem to lose the information I need.</p>
  
  <p>Maybe I'm just approaching this from the wrong angle?  From a performance standpoint, the loops ought to perform in roughly the same order as the original example.</p>
  
  <p>Any ideas of how to do this in a more compact functional representation?</p>