Linq GroupBy converted to a conditional? - c#

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();

Related

How to Make a default value for IGrouping<TKey, TSource> with count = 0?

I like C# linq and also the extension methods style.
Here is a simple code to get how many times of each number is there in an array:
static void Main(string[] args)
{
int[] nums = { 1, 2, 2, 3, 3, 3 };
var groups = nums.GroupBy(n => n);
//var keynums = nums.Distinct();//ok
var keynums = Enumerable.Range(0, 10);//causes ArgumentNullException
var timesDict = keynums.ToDictionary(n => n,
n =>
groups.FirstOrDefault(g => g.Key == n)
//((groups.FirstOrDefault(g => g.Key == n))??what can be put here)
.Count());
foreach (var kv in timesDict)
{
Console.WriteLine($"{kv.Key}\t{string.Join(" ", kv.Value)}");
}
Console.ReadKey();
}
The code works, but if I want know the nums are always [0-9], and want to get how many times [0-9] appears (if not appears, the count should be 0).
So the code will get ArgumentNullException, which makes sense because FirstOrDefault gets null.
So to fix this, I want to use the ?? operator, and give it a default value. but I cannot think of how to construct such value.
How would you solve it? please do not use other styles such as if, select new {}.
How about using C#6 null-propagation like this?
groups.FirstOrDefault(g => g.Key == n)?.Count() ?? 0
if FirstOrDefault returns null, ?.Count() will not be evaluated anymore and not throw an exception.
You can use like this:
var higherLimits = new[] { 10, 20, 30 };
var ranges = items.GroupBy(item => higherLimits.First(higherLimits => higherLimits >= item));
This will avoid null issue altogether.

MySql ORDER BY FIELD() in EntityFramework

I have a list of numbers and need to select rows from DB table by that order.
i was looking to implement ORDER BY FIELD() query in LinQ / lambda expressions with no luck
any ideas?
the code look like this:
using (var db = new TimeTable.EntityFramework.TimeTableEntities())
{
List<int> list = new List<int>() { 2, 1, 4, 3 };
var query = db.place_users_info.OrderBy(item => item.UniqueId);
}
I need to order the rows by the list items
if your rowcount is not that big, maybe you are looking for something like this (checks ommitted):
using (var db = new TimeTable.EntityFramework.TimeTableEntities())
{
List<int> list = new List<int>() { 2, 1, 4, 3 };
var a = db.place_users_info.ToArray();
var b = list.Select(x=>a[x]).ToList();
}
From what I understand you have (according to the example) 4 rows which you want to order by the number they have on the number list.
To do so use Zip to merge the lists by index (first item in numbers list will be with first item in the data list and so on)
using (var db = new TimeTable.EntityFramework.TimeTableEntities())
{
List<int> list = new List<int>() { 2, 1, 4, 3 };
var query = db.place_users_info.Zip(list, (f,s) => new { f,s })
.OrderBy(item => item.s)
.Select(item => item.f);
}
If you have the list, say:
var list<myobject> = <yourRepository>.<GetYourData>.ToList().OrderBy(o => o.<property>;
besides, in memory order by is probably faster then in DB.
If you would do:
var list<myobject> = <yourRepository>.<GetYourData>.OrderBy(o => o.<property>).ToList();
you would do the order by in the DB. (If your returns an IQueryable)

Get Random item from table without repetition

I am creating an application where I have to display a question from a list without repetition.
public IEnumerable<dynamic> GetQue()
{
var result = obj.tblQuestions
.OrderBy(r => Guid.NewGuid())
.Select(o => new { o.id, o.Question, o.Opt1, o.Opt2, o.Opt3, o.Opt4 })
.Take(1);
return result;
}
Currently I am getting a random question but with repetition. How do I get a record without repetition?
As I said in the comment, you can get elements one by one, using a random, and then remove the selected elements from list. Repeat this until the list is empty.
I am not gving yu exactly the code necessary for your case, you will still need to adapt it to your classes, but this is the principle it shoud respect:
var list = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int randomId;
Random rand = new Random();
if (list.Count != 0)
{
randomId = rand.Next(list.Count);
var randomElement = list[randomId];
list.RemoveAt(randomId);
return randomElement;
}
This gets the random elements from a list of integers, considering your list is the data iside a class, not the one you should renew, of course.
public ActionResult GetNextQuestion(int[] prevs = null)
{
var que = GetQue(prevs);
var ids = new int[] { que.id};
if(prevs != null)
ids = ids.Concat(prevs);
ViewBag.list = ids;
return View(que);
}
public dynamic GetQue(int[] prevs = null)
{
using (var obj = new Db())
{
var result = obj.tblQuestions;
if(prevs != null)
result = result.Where(e => !prevs.Contains(e.id));
result = result.OrderBy(r => new Guid())
.Select(o => new { o.id, o.Question, o.Opt1, o.Opt2, o.Opt3, o.Opt4 });
return result.First();
}
}
Source:how to avoid number repeation by using random class in c#?
If you add the items to a list as you cycle them, you can check the list to see if its been added or not. I'm pretty rookie, so i cant really code it out for you, but the idea is there. Make a seperate list for the entries you've already cycled through, then do maybe an if statement to check if the next entry is in the list before executing it.
I would have done this in a comment, but i dont have 50 rep, so i cant start a comment chain. :/

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