Linq to select highest ID where the ID is alphanumeric - c#

I have a list of ID where the ID's start with MB (for members) or NM (for non members).
The ID would be like MB-101 or NM-108 etc...
I am trying to select the Highest ID starting with MB and then Add 1 and then save back to DB. Saving is easy but I am not sure how to query the highest Member or Nonmember ID and add one to it. Any help is much appreciated

You can do something like this:
List<string> list = new List<string>() { "MB-101", "MB-102", "MB-103", "MB-104"};
var ids = list.Select(x =>Convert.ToInt32(x.Replace("MB-", "")));//convert all the number parts to integer
list[list.FindIndex(x => x == "MB-" + ids.Max().ToString())] = "MB-" + (ids.Max() + 1);//set the max number after adding one.
You can do the same with your Nonmember ID. It is tested code, it successfully addresses your problem.
Hope it helps.

You can get the max id by splitting your list like below:
var ids = yourList.Where(x => x.ID.StartsWith("MB-")).Select(x => new { ID = x.ID.Split('-')[1] }).ToList();
var maxIdValue = ids.Select(x => int.Parse(x.ID)).ToList().Max();
If you want max id from both starting with MB- and NB- than you can remove above where condition. By this it will fetch max id from both MB- and NB-. Following will be query than:
var ids = yourList.Select(x => new { ID = x.ID.Split('-')[1] }).ToList();
var maxIdValue = ids.Select(x => int.Parse(x.ID)).ToList().Max();

you can try like this
List<string> lststr = new List<string>() { "MB-101", "MB-103", "MB-102", "NM-108" };
var result = "MB-"+ lststr
.Where(x=>x.Contains("MB"))
.Select(x => Regex.Replace(x, #"[^\d]", ""))
.OrderByDescending(x=>x)
.FirstOrDefault();
it will return MB-103 because it will first check if the string contains MB then it will replace everything with "" other than digit and OrderByDescending it will order by Descending so that the highest value will be on top and at last FirstOrDefault will get the fist value

You have to do OrderByDescending your list by replacing MB or NM with empty and get FirstOrDefault from ordered list. Please check below example.
CODE:
List<string> list = new List<string>() { "MB-101", "MB-102", "MB-109", "MB-105", "NM-110"};
var maxMember = list.OrderByDescending(m=>Convert.ToInt16(m.Replace("MB-","").Replace("NM-",""))).ToList().FirstOrDefault();
Console.WriteLine(maxMember.ToString());

Related

Windows form, Which is a proper data structure definition for receiving/storing IDs and returning an integer?

I'm building a windows Form app(I'm a newbie).
After an event is raised, I have to save the given object ID, store it and count the occurrence of it.
The ID is of a string type. Is like a serial number.
Which is a proper data structure definition for receiving/storing IDs and returning an integer(the occurrence of the object).
Do I need an array/dictionary that manage the storing and the count?
Use a Dictionary of object ID to count:
Dictionary<int,int> objectIds = new Dictionary<int,int>();
Then when you get an object id, add it if it doesn't exist or increment the count if it does:
if (objectIds.ContainsKey(objectId))
{
objectIds[objectId]++;
}
else
{
objectIds.Add(objectId, 1);
}
You can use either a List or array to store IDs, and then count occurance of IDs using linq query as shown below:
var listOfIds = new List<string> {"a", "b", "a", "c", "a", "b"};
var query = from x in listOfIds
group x by x into g
let count = g.Count()
orderby count descending
select new {Value = g.Key, Count = count};
// Console.WriteLine is only used to show you how to access value and key inside your query
foreach (var x in q)
Console.WriteLine("Value: " + x.Value + " Count: " + x.Count);

SQL to LINQ expres

I'm trying to convert a SQL expression to Linq but I can't make it work, does anyone help?
SELECT
COUNT(descricaoFamiliaNovo) as quantidades
FROM VeiculoComSeminovo
group by descricaoFamiliaNovo
I try this:
ViewBag.familiasCount = db.VeiculoComSeminovo.GroupBy(a => a.descricaoFamiliaNovo).Count();
I need to know how many times each value repeats, but this way it shows me how many distinct values ​​there are in the column.
You can try:
var list = from a in db.VeiculoComSeminovo
group a by a.descricaoFamiliaNovo into g
select new ViewBag{
familiasCount=g.Count()
};
or
var list = db.VeiculoComSeminovo.GroupBy(a => a.descricaoFamiliaNovo)
.Select (g => new ViewBag
{
familiasCount=g.Count()
});
If you need column value:
new ViewBag{
FieldName=g.Key,
familiasCount=g.Count()
};
You don't need the GROUP BY unless there are fields other than the one in COUNT. Try
SELECT
COUNT(descricaoFamiliaNovo) as quantidades
FROM VeiculoComSeminovo
UPDATE, from your comment:
SELECT
COUNT(descricaoFamiliaNovo) as quantidades,
descricaoFamiliaNovo
FROM VeiculoComSeminovo
GROUP BY descricaoFamiliaNovo
That's it as SQL. In LINQ it is something like:
var reponse = db.VeiculoComSeminovo.GroupBy(a => a.descricaoFamiliaNovo)
.Select ( n => new
{Name = n.key,
Count = n.Count()
}
)
Not tested.
Ty all for the help.
I solved the problem using this lines:
// get the objects on db
var list = db.VeiculoComSeminovo.ToList();
// lists to recive data
List<int> totaisFamilia = new List<int>();
List<int> totaisFamiliaComSN = new List<int>();
// loop to cycle through objects and add the values ​​I need to their lists
foreach (var item in ViewBag.familias)
{
totaisFamilia.Add(list.Count(a => a.descricaoFamiliaNovo == item && a.valorSeminovo == null));
totaisFamiliaComSN.Add(list.Count(a => a.descricaoFamiliaNovo == item && a.valorSeminovo != null));
}
The query was a little slow than I expected, but I got the data

How to perform group by on list [duplicate]

This question already has answers here:
Using Linq to group a list of objects into a new grouped list of list of objects
(5 answers)
Closed 9 years ago.
I have a list that contains string and value of that string but list contains multiple string of same name now i want to group name and add value of that name in single entry. for example
Name ..... Value
apple ----- 2
mango ----- 4
banana ---- 8
apple ----- 4
Now i want to add apple as a single entry.
May be this is what you want:
suppose you have a collection like this:
List<Test> fruits = new List<Test>() { new Test() { Name="Apple", value=3 }
, new Test() {Name="Apple",value=5 }
, new Test() {Name="Orange",value=5 }
};
then you can groupBy it and sum similar items like this:
var netFruits= fruits.GroupBy(s => s.Name)
.Select(s => new Test()
{
Name=s.Key,
value = s.Sum(b=>b.value)
});
where netFruits will have two entries
Apple 8
Orage 5
where Test is:
public class Test
{
public string Name { get; set; }
public int value { get; set; }
}
You can do groupby like this
yourlist.GroupBy(x=>x.YourField())
If I think I know what you want is retreiving distint values. Than this is how it can be do
List<String> MyListOfFruit = new List<String>()
{
{"Banana"},
{"Banana"},
{"Apple"}
};
List<String> GroupedListOfFruits = new List<String>();
GroupedListOfFruits.AddRange(MyListOfFruit.Distinct());
//Now GroupedListOfFruits contains two items: Banana and Apple.
Otherwise post a piece of your code with what kind of list you exactly have. Do you have list of KeyValuePair<String, int> maybe?
So they are separated with "-----"?
var groups = list.Select(str => str.Split(new[]{"-----"}, StringSplitOptions.None))
.Select(split => new { Name = split.First(), Value = split.Last() })
.GroupBy(x => x.Name);
foreach (var grp in groups)
{
Console.WriteLine("Name:{0} Values:{1}", grp.Key, string.Join(",", grp.Select(x => x.Value)));
}
However, it is not clear what you mean with "Now i want to add apple as a single entry".
list.Distinct();
make sure you have included System.Linq in yr namespace.
Since the number of - separating the value from the key seems to be variable, I guess using a Regex to split is more appropriate:
var query = yourlist.Select(x=>{
var arr = Regex.Split(x,#"[-]+");
return new{
Name = arr[0],
Value = Int32.Parse(arr[1])
};
})
.GroupBy(x=>x.Name)
.Select(x=> new{Name = x.Key, Value=x.Sum(y=>y.Value)});
In this way you'll have a new anonymous object, with property Name equal to your string, and property Value equal to the sum of the Values of the elements with such Name.
If, instead you want as a return a string with the sum of the values as value, you can replace the last select with:
.Select(x=> x.Key + " ----- " + x.Sum(y=>y.Value).ToString());
And if, you already have a list with a name and value field, just remove the first Select.

LINQ Group-by with complete object access

What I want is better explained with code. I have this query:
var items = context.Items.GroupBy(g => new {g.Name, g.Model})
.Where(/*...*/)
.Select(i => new ItemModel{
Name=g.Key.Name,
SerialNumber = g.FirstOrDefault().SerialNumber //<-- here
});
Is there a better way to get the serial number or some other property that is not used in the key? The only way I could think of is to use FirstOrDefault.
Why not just include the serial number as part of the key via the anonymous type you're declaring:
var items = context.Items.GroupBy(g => new {g.Name, g.Model, g.SerialNumber })
.Where(/*...*/)
.Select(i => new ItemModel {
Name=g.Key.Name,
SerialNumber = g.FirstOrDefault().SerialNumber //<-- here
});
Or, alternatively, make your object the key:
var items = context.Items.Where(...).GroupBy(g => g)
.Select(i => new ItemModel {...});
Sometimes it can be easier to comprehend the query syntax (here, I've projected the Item object as part of the key):
var items = from i in context.Items
group i by new { Serial = g.Serialnumber, Item = g } into gi
where /* gi.Key.Item.GetType() == typeof(context.Items[0]) */
select new ItemModel {
Name = gi.Key.Name,
SerialNumber = gi.Key.Serial
/*...*/
};
EDIT: you could try grouping after projection like so:
var items = context.Items.Where(/*...*/).Select(i => new ItemModel { /*...*/})
.GroupBy(g => new { g.Name, g.Model });
you get an
IGrouping<AnonymousType``1, IEnumerable<ItemModel>> from this with your arbitrary group by as the key, and your ItemModels as the grouped collection.
I would strongly advise against what you're doing. The serial number is being chosen arbitrarily since you do no ordering in your queries. It would be better if you specified exactly which serial number to choose that way there are no surprises if the queries return items in a different ordering than "last time".
With that said, I think it would be cleaner to project the grouping and select the fields you need and take the first result. They all will have the same key values so that will stay the same, then you can add on any other fields you want.
var items = context.Items.GroupBy(i => new { i.Name, i.Model })
.Where(/*...*/)
.Select(g =>
g.OrderBy(i => i.Name).Select(i => new ItemModel
{
Name = i.Name,
SerialNumber = i.SerialNumber,
}).FirstOrDefault()
);
Since you need all the data, you need to store all the group data into your value (in the KeyValuePair).
I don't have the exact syntax in front of me, but it would look like:
/* ... */
.Select(g => new {
Key = g.key,
Values = g
});
After that, you can loop through the Key to get your Name group. Inside of that loop, include a loop through the Values to get your ItemModel (I guess that's the object containing 1 element).
It would look like:
foreach (var g in items)
{
Console.WriteLine("List of SerialNumber in {0} group", g.Key);
foreach (var i in g.Values)
{
Console.WriteLine(i.SerialNumber);
}
}
Hope this helps!
You might want to look at Linq 101 samples for some help on different queries.
if the serial number is unique to the name and model, you should include it in your group by object.
If it is not, then you have a list of serials per name and model, and selecting firstordefault is probably plain wrong, that is, I can think of no scenario you would want this.

Entity Framework - Join to a List

I need to retrieve a list of entities from my database that matches a list of items in a plain list (not EF). Is this possible with Entity Framework 4.1?
Example:
var list = new List<string> { "abc", "def", "ghi" };
var items = from i in context.Items
where list.Contains(i.Name)
select i;
This works great to return rows that match one property, but I actually have a more complex property:
var list = new List<Tuple<string, string>>
{
new Tuple<string,string>("abc", "123"),
new Tuple<string,string>("def", "456")
};
// i need to write a query something like this:
var items = from i in context.Items
where list.Contains(new Tuple<string,string>(i.Name, i.Type))
select i;
I know that is not valid because it will say it needs to be a primitive type, but is there any way to do what I'm trying to accomplish or will I need to resort to a stored procedure?
You have a few options:
1) You could, of course, write a stored procedure to do what you need and call it.
2) You could read the table into memory and then query the in memory list...that way you don't have to use primitives:
var items = from i in context.Items.ToList()
where list.Contains(new Tuple<string, string>(i.Name, i.Type))
select i;
3) You could also convert your query to use primitives to achieve the same goal:
var items = from i in context.Items
join l in list
on new { i.Name, i.Type } equals
new { Name = l.Item1, Type = l.Item2 }
select i;
I would go with the second option as long as the table isn't extremely large. Otherwise, go with the first.
You need to break it down to sub-properties. For example, something like (this might not compile, i'm not able to test at the moment, but it should give you something to work with):
var items = from i in context.Items
where list.Select(x => x.Item1).Contains(i.Name)
&& list.Select(x => x.Item2).Contains(i.Type)
select i;
You have to think about what the resulting SQL would look like, this would be difficult to do directly in SQL.
My suggestion would be you split out one field of the tuples and use this to cut down the results list, get back the query result then filter again to match one of the tuples e.g.
var list = new List<string> { "abc", "def" };
var list2 = new List<Tuple<string, string>>
{
new Tuple<string,string>("abc", "123"),
new Tuple<string,string>("def", "456")
};
var items = (from i in context.Items
where list.Contains(i.Name)
select i)
.AsEnumerable()
.Where(i => list2.Any(j => j.val1 == i.Name && j.val2 == i.Type);

Categories

Resources