Convert LINQ query to List - c#

Here is function, that I use the list of permutations:
public static IEnumerable<IEnumerable<T>> Permute<T>(IEnumerable<T> list)
{
if (list.Count() == 1)
return new List<IEnumerable<T>> { list };
return list.Select((a, i1) => Permute(
list.Where((b, i2) => i2 != i1)).Select(
b => (new List<T> { a }).Union(b))
).SelectMany(c => c);
}
use it in the following way:
var SFP_vars = Permute(SFP);
Where SFP is array of bytes:
byte[] SFP = new byte[7] { 0, 1, 2, 3, 4, 5, 6 };
There is other variable:
List<byte[]> lst_SFP = new List<byte[]>();
Now my question: How to assign the value of SFP_vars to lst_SFP ?
lst_SFP = SFP_vars.ToList() doesn't work.

You need to also convert the inner enumerables to byte arrays:
lst_SFP = SFP_vars.Select(x => x.ToArray()).ToList();

lst_SFP = SFP_vars.Select(bytePermutation => bytePermutation.ToArray()).ToList()
Basically you want the things in your list to be a byte array, so the Select statement handles that, giving you an IEnumerable<byte[]>.
Then we do ToList on that to make it a list.
bytePermutation could be named anything, I just named it as such so you can get an idea of what's going on.

SFP_vars looks like an IEnumerable<IEnumerable<byte>> rather than an IEnumerable<byte[]>.
This means that the you first need to convert the IEnumerable<byte> objects to byte[] using the ToArray() method. You can then add them to the list.
Although it amounts to the same thing, you could replace with this LINQ query:
List<byte[]> lst_SFP = SFP_vars.Select(ie => ie.ToArray()).ToList();
Hope this helps.

You need to convert the inner IEnumerable<byte> into an array, this can be done using .Select(). One this is done .ToList() will return as a List<byte[]>.
var SFP = new byte[7] { 0, 1, 2, 3, 4, 5, 6 };
var SFP_vars = Permute(SFP);
var lst_SFP = SFP_vars
.Select(e => e.ToArray())
.ToList();
Unless you need a particular order though, you may be better off using the List<IEnumerable<byte>>. Using this will prevent an Array.Copy.

Related

Linq GroupBy converted to a conditional?

If I have the following List
List<int> Values = new List<int>() { 1, 5, 6, 2, 5 };
and I want to check for duplicates, so I use GroupBy:
Values.GroupBy(x => x).Where(g => g.Count() > 1).Select(g => g.Key)
How do I make this work in a While conditional/convert to boolean so that, if and only if there are any duplicates present in Values, evaluate to true, otherwise, evaluate false using Linq? I suspect I need to use .Any somehow, but I'm not sure where to fit it in.
Simply check before adding randomly generated number in the list
do
{
var randomNumber = //Generate number here
if(!Values.Contains(randomNumber))
{
Values.Add(randomNumber);
break;
}
}while(true);
The root problem here, is that you are using a Select call. The Select call you are using will return an IEnumerable of longs, not a boolean.
The only way code like that would compile as part of a boolean (be it as a bool variable or as a check done inside an If, While, etc), is if you remove both the Select and the Where calls, and replace it with an Any:
List<int> Values = new List<int>() { 1, 5, 6, 2, 5 };
Values.GroupBy(x => x).Any(g => g.Count() > 1);
If you then need to find out what the duplicates actually are, then the best way to do this would be to store the results of the Group call in a variable, and then use that variable to call Any in your boolean check, and grab the items from the variable when you want to report on them:
List<int> Values = new List<int>() { 1, 5, 6, 2, 5 };
var duplicateValues = Values.GroupBy(x => x).Where(g => g.Count() > 1);
bool anyDuplicates = duplicateValues.Any();
var duplicateKeys = duplicateValues.Select(x => x.Key).ToList();
You can use combination of foreach loop and return value of HashSet<int>.Add method.
With this approach you don't need to loop all values if duplication have been found early. Where GroupBy should always loop all values.
public static bool HaveDuplicates(this IEnumerable<int> values)
{
var set = new HashSet<int>();
foreach (var value in values)
{
if (set.Add(value) == false)
{
return true;
}
}
return false;
}
Use it as extension method
var values = new List<int>() { 1, 5, 6, 1, 2, 5, 6, 7, 8, 9 };
if (values.HaveDuplicates())
{
// have duplicates
}
I'm trying to use it in a do while loop. Inside the do, I generate a
random number from 1 to 9, and add it to the Values list. if the
number that gets generated is one already in the list, then run the
loop over again. If not, escape the loop
Use HashSet<T> for saving generated number. HashSet<T> will check for duplication with O(1) operations, where
var values = new HashSet<int>();
do
{
var generatedValue = GenerateNumber(); // your generation logic
if(values.Add(generatedValue) == false)
{
break;
}
}
while(yourCondition);
You can loop HashSet as other collections or convert it to List if you need
var numbers = values.ToList();

Using C# lambdas to combine List<int> and int

I have the following list of Pair objects:
var listOfPairs = new List<Pair<int, List<int>>>() {
new Pair<int, List<int>>(30, new List<int>() {3, 6, 9}),
new Pair<int, List<int>>(40, new List<int>() {4, 8, 12})
};
I would like to end up with the following list of list-of-integers:
listOfPairs[0] = {30, 3, 6, 9};
listOfPairs[1] = {40, 4, 8, 12};
I've tried a lot of fiddling that looks like this, but to no avail:
var flattenedListOfPairs = listOfPairs.Select(pair => new List<int>(pair.First).AddRange(pair.Second));
I assume that what I'm trying to do is possible, and I'm just missing something.
Sounds like you might want something like:
var flattened = listOfPairs.Select(pair => new[] { pair.First }.Concat(pair.Second)
.ToList())
.ToList();
Or:
var flattened = listOfPairs.Select(pair => Enumerable.Repeat(pair.First, 1)
.Concat(pair.Second)
.ToList())
.ToList();
Or using MoreLINQ
var flattened = listOfPairs.Select(pair => pair.Second.Prepend(pair.First)
.ToList())
.ToList();
This gives you a list of lists, in the form you specified:
listOfPairs.Select(p => new []{ p.First }.Concat(p.Second).ToList()).ToList()
Other answers already covered how to do this, so I won't repeat that here. This answer is to explain why your existing code wasn't working. You expected to pass an int to the List constructor and have it initialize the List with that int. That's not how the constructor works. The List constructor uses the int argument to set up the initial size of the list, rather than set the value of any items.
Try this:
var flattenedListOfPairs = listOfPairs.Select(pair =>
{
var list = new List<int>(pair.First);
list.AddRange(pair.Second));
return list;
}.ToList();

Working on lambda expression

I am squaring each integer in a List. Here is the code.
class SomeIntgs
{
List<int> newList = new List<int>();
public List<int> get()
{
IEnumerable<int> intrs = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 };
newList.AddRange(intrs);
return newList;
}
}
I am getting error in Main()
SomeIntgs stg = new SomeIntgs();
var qry = from n in stg.get() where (P => P*P) select n;
Error : "Can not convert lambda expression to type bool ".
Help Please.
Also help me, how can i handle lambda in general context
You don't need the where, try this:
SomeIntgs stg = new SomeIntgs();
var qry = from n in stg.get() select n*n;
or
var qry = stg.get().Select(P => P*P);
Enumerable.Where is used to filter elements from a sequence - what you really want to do is project a new sequence of elements like I have shown above.
The lambda that the where clause takes specifies how you match an item from your IQueryable. Any member of the IQueryable that satisfies the expression you supply will be returned. (This is why your compiler is complaining about bools).
As others have mentioned, you can drop the where clause to square each item in the list.
var ints = new int []{1,2,3,4,5,6,7,8};
var squares = ints.Select(x => x*x);
var evenSquares = ints.Where(x => (x % 2) == 0).Select(x => x*x); // only square
//the even numbers in the list
SomeIntgs stg = new SomeIntgs();
var qry = from n in stg.get() select n*n;

LINQ: Determine if two sequences contains exactly the same elements

I need to determine whether or not two sets contains exactly the same elements. The ordering does not matter.
For instance, these two arrays should be considered equal:
IEnumerable<int> data = new []{3, 5, 6, 9};
IEnumerable<int> otherData = new []{6, 5, 9, 3}
One set cannot contain any elements, that are not in the other.
Can this be done using the built-in query operators? And what would be the most efficient way to implement it, considering that the number of elements could range from a few to hundreds?
If you want to treat the arrays as "sets" and ignore order and duplicate items, you can use HashSet<T>.SetEquals method:
var isEqual = new HashSet<int>(first).SetEquals(second);
Otherwise, your best bet is probably sorting both sequences in the same way and using SequenceEqual to compare them.
I suggest sorting both, and doing an element-by-element comparison.
data.OrderBy(x => x).SequenceEqual(otherData.OrderBy(x => x))
I'm not sure how fast the implementation of OrderBy is, but if it's a O(n log n) sort like you'd expect the total algorithm is O(n log n) as well.
For some cases of data, you can improve on this by using a custom implementation of OrderBy that for example uses a counting sort, for O(n+k), with k the size of the range wherein the values lie.
If you might have duplicates (or if you want a solution which performs better for longer lists), I'd try something like this:
static bool IsSame<T>(IEnumerable<T> set1, IEnumerable<T> set2)
{
if (set1 == null && set2 == null)
return true;
if (set1 == null || set2 == null)
return false;
List<T> list1 = set1.ToList();
List<T> list2 = set2.ToList();
if (list1.Count != list2.Count)
return false;
list1.Sort();
list2.Sort();
return list1.SequenceEqual(list2);
}
UPDATE: oops, you guys are right-- the Except() solution below needs to look both ways before crossing the street. And it has lousy perf for longer lists. Ignore the suggestion below! :-)
Here's one easy way to do it. Note that this assumes the lists have no duplicates.
bool same = data.Except (otherData).Count() == 0;
Here is another way to do it:
IEnumerable<int> data = new[] { 3, 5, 6, 9 };
IEnumerable<int> otherData = new[] { 6, 5, 9, 3 };
data = data.OrderBy(d => d);
otherData = otherData.OrderBy(d => d);
data.Zip(otherData, (x, y) => Tuple.Create(x, y)).All(d => d.Item1 == d.Item2);
First, check the length. If they are different, the sets are different.
you can do data.Intersect(otherData);, and check the length is identical.
OR, simplt sort the sets, and iterate through them.
First check if both data collections have the same number of elements and the check if all the elements in one collection are presented in the other
IEnumerable<int> data = new[] { 3, 5, 6, 9 };
IEnumerable<int> otherData = new[] { 6, 5, 9, 3 };
bool equals = data.Count() == otherData.Count() && data.All(x => otherData.Contains(x));
This should help:
IEnumerable<int> data = new []{ 3,5,6,9 };
IEnumerable<int> otherData = new[] {6, 5, 9, 3};
if(data.All(x => otherData.Contains(x)))
{
//Code Goes Here
}

How to get the top 3 elements in an int array using LINQ?

I have the following array of integers:
int[] array = new int[7] { 1, 3, 5, 2, 8, 6, 4 };
I wrote the following code to get the top 3 elements in the array:
var topThree = (from i in array orderby i descending select i).Take(3);
When I check what's inside the topThree, I find:
{System.Linq.Enumerable.TakeIterator}
count:0
What did I do wrong and how can I correct my code?
How did you "check what's inside the topThree"? The easiest way to do so is to print them out:
using System;
using System.Linq;
public class Test
{
static void Main()
{
int[] array = new int[7] { 1, 3, 5, 2, 8, 6, 4 };
var topThree = (from i in array
orderby i descending
select i).Take(3);
foreach (var x in topThree)
{
Console.WriteLine(x);
}
}
}
Looks okay to me...
There are potentially more efficient ways of finding the top N values than sorting, but this will certainly work. You might want to consider using dot notation for a query which only does one thing:
var topThree = array.OrderByDescending(i => i)
.Take(3);
Your code seems fine to me, you maybe want to get the result back to another array?
int[] topThree = array.OrderByDescending(i=> i)
.Take(3)
.ToArray();
Its due to the delayed execution of the linq query.
As suggested if you add .ToArray() or .ToList() or similar you will get the correct result.
int[] intArray = new int[7] { 1, 3, 5, 2, 8, 6, 4 };
int ind=0;
var listTop3 = intArray.OrderByDescending(a=>a).Select(itm => new {
count = ++ind, value = itm
}).Where(itm => itm.count < 4);

Categories