[转]7个linq技巧
原文很容易懂就不翻了
Often, you need to initialize elements of an array to either the same value, or to an increasing sequence values, or possibly to a sequence increasing or decreasing by a step different from one. With LINQ, you can do all of this within the array initializer - no for loops necessary!
In the following code sample, the first line initializes a to an array of length 10 with all elements set to -1, the second line initializes b to (0,1,..9), and the third line initializes c to (100,110,…,190):
int[] a = Enumerable.Repeat(-1, 10).ToArray(); int[] b = Enumerable.Range(0, 10).ToArray(); int[] c = Enumerable.Range(0, 10).Select(i => 100 + 10 * i).ToArray();
A word of caution: if you are initializing large arrays, you may want to forego the elegance and use the old-fashioned for loop instead. The LINQ solution will grow the array dynamically, so garbage arrays will need to be collected by the runtime. That said, I use this trick all the time when initializing small arrays, or in testing/debugging code.
2. Iterate over multiple arrays in a single loop
A friend asked me a C# question: is there a way to iterate over multiple collections with the same loop? His code looked something like this:
foreach (var x in array1) { DoSomething(x); } foreach (var x in array2) { DoSomething(x); }
In his case, the loop body was larger, and he did not like the duplicated code. But, he also did not want to allocate a new array to hold elements from both array1 and array2.
LINQ provides an elegant solution to this problem: the Concat operator. You can rewrite the above two loops with a single loop as follows:
foreach (var x in array1.Concat(array2)) { DoSomething(x); }
Note that since LINQ operates at the enumerator level, it will not allocate a new array to hold elements of array1 and array2. So, on top of being rather elegant, this solution is also space-efficient.
This is a simple trick to generate a random sequence of length N:
Random rand = new Random(); var randomSeq = Enumerable.Repeat(0, N).Select(i => rand.Next());
Thanks to the lazy nature of LINQ, the sequence is not pre-computed and stored in an array, but instead random numbers are generated on-demand, as you iterate over randomSeq.
LINQ is also a nice tool to generate various kinds of strings. I found this quite useful to generate strings for testing and debugging purposes.
Let’s say that you want to generate a string with the repeating pattern "ABCABCABC…" of length N. Using LINQ, the solution is quite elegant:
string str = new string( Enumerable.Range(0, N) .Select(i => (char)('A' + i % 3)) .ToArray());
[EDIT] Petar Petrov suggested another interesting way to generate strings with LINQ. His approach applies to different scenarios than my solution above:
string values = string.Join(string.Empty, Enumerable.Repeat(pattern, N).ToArray());
5. Convert sequences or collections
One thing you cannot do in C# or VB is to cast a sequence of type T to a sequence of type U, even if T us a derived class from U. So, you cannot just simply cast List<string> to List<object>. (For an explanation why, see Bick Byers’ posting).
But, if you are trying to convert IEnumerable<T> to IEnumerable<U>, LINQ has a simple and efficient solution for you:
IEnumerable<string> strEnumerable = ...; IEnumerable<object> objEnumerable = strEnumerable.Cast<object>();
If you need to convert List<T> to List<U>, there is also a simple LINQ solution, but it involves copying the list:
List<string> strList = ...; List<object> objList = strList.Cast<object>().ToList();
6. Convert a value to a sequence of length 1
When you need to convert a single value to a sequence of length 1, what do you do? You could construct an array of length 1, but I prefer the LINQ Repeat operator:
IEnumerable<int> seq = Enumerable.Repeat(myValue, 1);
7. Iterate over all subsets of a sequence
Sometimes it is useful to iterate over all subsets of an array. This situation arises quite frequently in brute-force solutions to hard problems. For small inputs, subset sum, boolean satisfiability and theknapsack problem can all be solved easily by iterating over all subsets of some sequence.
In LINQ, we can generate all subsets of array arr as follows:
T[] arr = ...; var subsets = from m in Enumerable.Range(0, 1 << arr.Length) select from i in Enumerable.Range(0, arr.Length) where (m & (1 << i)) != 0 select arr[i];
Note that if the number of subsets overflows an int, the above code will not work. So, only use it if you know that the length of arr is at most 30. If the length of arr is greater than 30, chances are that you don’t want to iterate over all of its subsets anyway because it is going to take minutes or more.
</SPAN< span>