Find the first free id - c#

One of my small database management projects (written in delphi) used sql queries to find the first free id of mysql table.
Example: I have to find the first free id (hole) in a table like this:
| id | Col1 |
|------|------|
| 5101 | ABC |
| 5102 | BCD |
| 5103 | CDE |
| 5105 | EFG | 🡔 first missing id
| 5106 | GHI |
| 5108 | ILM |
The code should find the first free id 5104
Here's how I'd do it in SQL (in old project):
SELECT
MIN((doc.id + 1)) AS nextID
FROM (doc
LEFT JOIN doc doc1
ON (((doc.id + 1) = doc1.id)))
WHERE (ISNULL(doc1.id) AND (doc.id > 5000))
Now, which I am rewriting in c # language, I need to convert sql statements into a LINQ query (which uses Devart dotConnect for mysql Entity Framework).
Starting from here:
DC db = new DC();
var nums = db.Documentos.OrderBy(x => x.Id);

From Can LINQ be used to find gaps in a sorted list?:
var strings = new string[] { "7", "13", "8", "12", "10", "11", "14" };
var list = strings.OrderBy(s => int.Parse(s));
var result = Enumerable.Range(list.Min(), list.Count).Except(list).First(); // 9
Basically, order the list. Then create an array of sequential numbers (1,2,3...) from the minimum all the way to the max. Check for missing values in the list, and grab the first one. That's the first missing number.

This can give you all gaps within your table
var nums= (new List<int> (){1,2,3,25,4,5,6,7,8, 12, 15,21,22,23}).AsQueryable();
nums
.OrderBy(x => x)
.GroupJoin(nums, n=> n + 1, ni => ni, (o,i)=> new {o, i})
.Where(t=> !(t.i is IGrouping<int, int>))
.Dump();
.Net Fiddle

Another method (similar to what you're using now).
Assume you have an array of integers (or another type of collection) like this:
var myIDs = new int[] { 5101, 5113, 5102, 5103, 5110, 5104, 5105, 5116, 5106, 5107, 5108, 5112, 5114, 5115 };
If it's not already ordered, the OrderBy() it:
myIDs = myIDs.OrderBy(n => n).ToArray();
Extract the first number that is less than (next number) + 1:
int result = myIDs.Where((n, i) => (i < myIDs.Length - 1) && (n + 1 < myIDs[i + 1])).FirstOrDefault();
If none of the members of this collection satisfy the condition, take the last one and add 1:
result = result == default ? myIDs.Last() + 1 : result;

Related

Find the Biggest List in a Dictionary of Lists

Lets say I have:
Dictionary<string, List<string>> ourGroups = new Dictionary(string, List<string>>();
Dictionary contents (Key is based on a specific letter position as shown):
Key | Value | Total
-O- | "DOC", "HOP", "POP" | Total = 3
--- | "SIP", "PLZ", "SUZ", "UPS" | Total = 4
So key 2 contains our biggest list of values.
How would I place the biggest key Values into a separate list? What I tried:
List<string> finalGroup = new List<string>();
finalGroup = ourGroups.Values.Max();
Obviously I'm not doing this correctly. A bit of explanation on how this works would be appreciated.
You can use LINQ to sort by Values and select the first one like below .
var result = ourGroups.OrderByDescending(s => s.Value.Count()).First().Value;
If you intend to get the Key, which has Items in its Value (list), you could use
var keyWithMaxValue = ourGroups.Aggregate((l, item) => l.Value.Count > item.Value.Count ? l : item).Key;
If you need the Value (list)
var maxValue = ourGroups.Aggregate((l, item) => l.Value.Count > item.Value.Count ? l : item).Value;

Create a DataTable containing only unique values with Linq in C#

I have a DataTable dt_Candidates
Candidate | First Name | Last Name
--------------------|----------------|---------------
John, Kennedy | John | Kennedy
Richard, Nixon | Richard | Nixon
Eleanor, Roosevelt | Eleanor | Roosevelt
Jack, Black | Jack | Black
Richard, Nixon | Richard | Nixon
I want to create without a nested loops and preferably using Linq, a DataTable containing ONLY unique values like this one called dt_Candidates2:
Candidate | First Name | Last Name
--------------------|----------------|---------------
John, Kennedy | John | Kennedy
Eleanor, Roosevelt | Eleanor | Roosevelt
Jack, Black | Jack | Black
And a list or an array called RejectedCandidates containing the distinct duplicates
RejectedCandidates = {"Richard, Nixon"}
As noted, I don't think it really needs LINQ here. It can go something like this:
DataTable dt = new DataTable();
dt.Columns.Add("Candidate");
dt.Columns.Add("First");
dt.Columns.Add("Last");
dt.PrimaryKey = new []{ dt.Columns["Candidate"] }; //means that dt.Find() will work
while(...){
string candidate = ...
if(dt.Rows.Find(candidate) != null)
RejectList.Add(...);
else
dt.Rows.Add(...);
}
Avoid using LINQ's .Any on a DataTable for this. Not only is it a pain to get going because it needs casting steps or extension libraries (see here) to, it will then use loops to find the info you seek; the built-in mechanism for the PrimaryKey uses hash tables for much faster lookups.
var dt = new DataTable
{
Columns = {"Candidate", "First Name", "Last Name"},
Rows =
{
new object [] { "John, Kennedy", "John", "Kennedy"},
new object [] { "Richard, Nixon", "Richard", "Nixon"},
new object [] { "Eleanor, Roosevelt", "Eleanor", "Roosevelt"},
new object [] { "Jack, Black", "Jack", "Black"},
new object [] { "Richard, Nixon", "Richard", "Nixon"},
}
};
you can use grouping (groupBy) to find duplicates, filter them out, and then create a new DataTable, using DataTableExtensions.CopyToDataTable extension method:
var dt2 = dt.AsEnumerable()
.GroupBy(r => r["Candidate"])
.Where(g => g.Count() == 1)
.Select(g => g.First())
.CopyToDataTable();

How to find key in Dictionary<DateTime, DataTable> by using Linq

I have a dictionary DateTime, DataTable
Key Values
2019/07/01 Column A | B
1 | 1
2019/07/02 Column A | B
3 | 4
2019/07/03 Column A | B
5 | 4
I would like to use linq to find the key of the dictionary with maximum value of column A in the dict.
In above case, my expected result is "2019/07/03"
I am new in linq. Is it possible for above use? How to do?
While .OrderByDescending(x=> x.Key).FirstOrDefault()?.Key; will give you the correct answer, it will result in first sorting the values which is time and memory inefficient.
What you want to do is scan all your values once without memory consumption:
KeyValuePair<MyKey, MyValue> maxKey;
if (list.Count > 0)
{
maxKey = list.Aggregate(list.First(), (first,second)=>first.Value.A>second.Value.A?first:second);
}
Another one example.
Dictionary<DateTime, int> example = new Dictionary<DateTime, int>()
{
{new DateTime(2019,07,01), 1 },
{new DateTime(2019,07,02), 2 },
{new DateTime(2019,07,03), 3 }
};
var HighestKey = example.Keys.Max();
Please try below solution, I am sure it will work.
<DictionaryName>.OrderByDescending(x => x.Key).First();

LINQ: analog for sql's EXISTS?

Let's say I have a few people in a collection "Employees":
Name | Position | Age
------+----------+----
Tom | Manager | 35
Hank | Driver | 38
Harry | Driver | 45
... | ... | ...
Mark | Driver | 30
----------------------
and I want to get all Drivers if there is at least one among them who's older that 40 years old.
Could you help me to complete my LINQ?
UPD. I'd like to do this task using a single LINQ and perfomance has no matter. So decision
var allDrivers = Employees.Where(n => n.Position == "Driver").ToList();
return allDrivers.Any(n => n.Age > 40)
? allDrivers
: new List<Employee>();
is good, but I can't mark it as an answer.
In this particular case I need to get Hank, Harry and Mark. Because all they're drivers and Harry is 45 (>40). But if Harry was 39 I would get nothing as a result because in this case all drivers were under 40.
So if I were to parse "I want to get all Drivers if there is at least one among them who's older that 40 years old" literally, it'd be
var allDrivers = Employees.Where(n => n.Position == "Driver").ToList();
return allDrivers.Any(n => n.Age > 40)
? allDrivers
: new List<Employee>();
Or something similar.
For one-query functional craziness:
(int maxAge, List<Employee> result) = Employees
.Aggregate(
(age: 0, list: new List<Employee>()),
(al, n) => n.Position == "Driver"
? (Math.Max(al.age, n.Age), al.list.Concat(new [] {n}).ToList()
: al));
return maxAge > 40 ? result : new List<Employee>();
This is just PoC that you CAN. But remember - when asking yourself if you could, don't forget to ask yourself if you should :)
If you really want it inside one statement (this feels very artificial to me) then you can do this:
IEnumerable<Employee> result =
employees.GroupBy(n => n.Position)
.Where(g => g.Key == "Driver" && g.Max(x => x.Age) > 40)
.FirstOrDefault();
Don't forget to do
return result ?? Enumerable.Empty<Employee>();
if you want to return an empty result instead of null when nothing is found.

LINQ to SQL selective choosing of fields from a group

I have a LINQ to SQL query which at one point, returns two rows which I'd like to group by ID and select specific fields, lets say something like this:
ID | Field1 | Field2 | Field3
-----------------------------
1 | 2 | null | 4
1 | 3 | 5 | null
What's unusual about this query is that from the group, I want to select something like this:
ID | Field1 | Field2 | Field3
-----------------------------
1 | 2 | 5 | 4
Note that the group selects fields from both records. Also note that it's not a simple null coalesce type operation as I need to be able to select a Field1 of 2 or 3 based on some other factors (another field).
Is there a clever way I can project into a new type whilst selecting specific fields as I want them?
I'm working on an IQueryable at this point, so I need a solution which translates into SQL, previously the query did something like this:
.Select(x => new MyObject {
Field1 = myGroup.First(x => x.Field4 == 1).Field1,
Field2 = myGroup.FirstOrDefault(x => x.Field4 == 1) == null ? myGroup.FirstOrDefault(x => x.Field4 == 2).Field2 : myGroup.FirstOrDefault(x => x.Field4 == 1).Field2
});
Which doesn't work as it can't work out what to with the operations.
I think we might be resigned to selecting some combination object with all the fields denormalised and then selecting from LINQ to objects in memory, but I'd rather have the DB do the work if possible.
What you need to do is write the C#/LINQ expression to generate a SQL CASE statement.
Generally speaking a case statement is written:
M = CASE
WHEN A == P THEN V
WHEN B == Q THEN W
WHEN C == R THEN X
WHEN D == S THEN Y
ELSE Z
END
In these examples all the letters A = P to D = S, and V to Z can be any expression of any complexity. So for example you can say WHEN a.cost > 1.5 * b.estimate THEN c.bestoffer.
To generate that exact case statement you need the following LINQ/C#:
M =
(
A == P? V :
B == Q? W :
C == R? X :
D == S? Y :
Z
)
So to attack your (unstated) problem directly, you can write arbitrary conditions for the values of each field, using ternary operators and these will be converted to LINQ CASE statements.
See also (and credit to) to the following:
http://www.codeproject.com/Articles/38264/How-to-Create-T-SQL-CASE-Statements-With-LINQ-To-S
linq case statement
If I understand what you're asking for, should be something like this:
from g in (from entity in db.Entities
group entity by entity.Id)
select new
{
Id = g.Key,
Field1 = g.Select(e => e.Field1)
.FirstOrDefault(f => null != f),
Field2 = g.Select(e => e.Field2)
.FirstOrDefault(f => null != f),
Field3 = g.Select(e => e.Field3)
.FirstOrDefault(f => null != f),
}
Note that if – for any given Fieldx – there is no non-null value of that field within that group, the resultant Fieldx value will be null.

Categories