MySql ORDER BY FIELD() in EntityFramework - c#

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)

Related

How do I sort a List<Type> by List<int>?

In my c# MVC project I have a list of items in that I want to sort in order of another list
var FruitTypes = new List<Fruit> {
new Fruit { Id = 1, Name = "Banana"},
new Fruit { Id = 2, Name = "Apple" },
new Fruit { Id = 3, Name = "Orange" },
new Fruit { Id = 4, Name = "Plum"},
new Fruit { Id = 5, Name = "Pear" },
};
SortValues = new List<int> {5,4,3,1,2};
Currently my list is showing as default of fruit type.
How can I sort the Fruit list by SortValues?
It's unclear if you are sorting by the indexes in SortValues or whether SortValues contains corresponding Id values that should be joined.
In the first case:
First you have to Zip your two lists together, then you can sort the composite type that Zip generates, then select the FruitType back out.
IEnumerable<FruitType> sortedFruitTypes = FruitTypes
.Zip(SortValues, (ft, idx) => new {ft, idx})
.OrderBy(x => x.idx)
.Select(x => x.ft);
However, this is simply sorting the first list by the ordering indicated in SortValues, not joining the ids.
In the second case, a simple join will suffice:
IEnumerable<FruitType> sortedFruitTypes = SortValues
.Join(FruitTypes, sv => sv, ft => ft.Id, (_, ft) => ft);
This works because Enumerable.Join maintains the order of the "left" hand side of the join.
While there is almost certainly a more LINQ-y way, if you tend towards verbosity, you could accomplish this with an iterator function. For example:
public IEnumerable<Fruit> SortFruits(IEnumerable<Fruit> unordered, IEnumerable<int> sortValues)
{
foreach (var value in sortValues)
yield return unordered.Single(f => f.Id == value);
}
I like that it's explicit about what it's doing. You may consider throwing an exception when the number of items in each list is different, or maybe you just don't return an item if there is no sort value for it. You'll have to decide what the behaviour should be for "missing" values in either collection is. I think that having to handle these scenarios is a good reason to put it all in a single method this way, instead of a longer LINQ query.
Time complexity:O(n) + TM of Linq.
Declare list of fruits to store result.
Iterate through each fruit type.
Use Linq FirstOrDefault to get element by sorted value.
List<int> SortValues = new List<int> { 5, 4, 3, 1, 2 };
List<Fruit> result = new List<Fruit>();
foreach (var element in SortValues)
{
Fruit f = FruitTypes.FirstOrDefault(fruitElement => fruitElement.Id == element);
result.Add(f);
}
Implementation: DotNetFiddler

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. :/

Simplify conventional foreach nested loop using linq & lambda expression

(See my code snippet below) I want to find all items of coll1 that matched to items of coll2 (number of items of coll2 <= number of items of coll1) and put the query result in coll3. How to achieve it using linq and lambda expression?
Surely, I can simply copy coll2 to coll3 :-) but that is not my goal. I want to know the ways using linq and lambda to replace such conventional logic construct. Thank you in advance.
var coll1 = new List<int>() { 1, 2, 3, 4, 5 };
var coll2 = new List<int>() { 2, 4 };
var coll3 = new List<int>();
foreach ( var selected in coll2 )
{
foreach ( var item in coll1 )
{
if ( selected == item )
{
coll3.Add(item);
}
}
}
You can use Intersect
coll1.Intersect(coll2);
But this wont work as expected(see King King's comment)..You can instead do this
coll2.Where(x=>coll1.Any(y=>x==y));
coll3 = coll1.Where(i => coll2.Contains(i)).ToList();
Update. A little bit simpler, as suggested in comments:
coll3 = coll1.Where(coll2.Contains).ToList();
As a first step, you can use a where clause to make your code more readable :
var coll1 = new List<int>() { 1, 2, 3, 4, 5 };
var coll2 = new List<int>() { 2, 4 };
var coll3 = new List<int>();
foreach (var selected in coll2)
{
coll3.AddRange(coll1.Where(item => selected == item));
}
Use Intersect: http://msdn.microsoft.com/en-us/library/bb460136.aspx
var coll3 = coll1.Intersect(coll2)
You could do this; not sure if it's more readable tho!
var coll3 = (from selected in coll2
from item in coll1
where selected == item
select item).ToList();
If you do not want to directly assign the result to your list, you could add 'ForEach' to your Linq statement:
coll1.Where(i => coll2.Contains(i)).ToList().ForEach(i => coll3.Add(i));
You might want to check for Distinct though

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

C#: Creating objects that does not exist in a different list

I have two lists of two different kinds of objects representing data rows from two sql queries. The first list contains data, and the second contains more detailed data. So as an example:
List1: List2:
1 Alice 1 15
2 Bob 1 19
3 Carol 2 5
4 Dave 2 7
2 20
4 16
I want to insert rows into List2 so that everyone in List1 has at least one row in List2. So when no rows exist in List2 for a certain person, I want to insert a single one with a default value. In the example case I would have to insert one row for Carol, so I would end up with:
List1: List2:
1 Alice 1 15
2 Bob 1 19
3 Carol 2 5
4 Dave 2 7
2 20
3 0
4 16
Does anyone have a clever, clean and efficient way of doing this?
I know that to join these tables together into one I would have to use an Outer Join, for example like in this Outer Join Sample. But I don't want a new result set. I just want those missing rows to be inserted into List2.
Note: Yes, I know the question\title is kind of... blah... but I don't know how to formulate it better. Someone please fix it if you can.
Note 2: I can not use SQL. I can not insert those rows in the original table. I am reporting on data, which means I do not touch any of the data. I just read it out. The data is to be used in a master-detail report, and my issue is that when no details exist for a certain master row, then you end up with just an empty space. Which is not good. So I want to insert rows with sensible info so that the user can see that there was nothing to show here.
Assuming your lists are sorted by the Key value like in your example (in this case an integer), something like this should work:
int i = 0;
foreach (var item in List1)
{
// Skip any items in List2 that don't exist in List1
// (not sure this is needed in your case), or that we've
// already passed in List1
while (List2[i].Key < item.Key)
i++;
if (List2[i].Key > item.Key)
{
// Create new item in List2
List2.Add(new List2Item(item.Key, 0));
}
}
// TODO: resort List2
Depending on how many items you expect to be missing, you might want to Insert into List2 instead, eliminating the need for the resorting. If you expect a lot of items to be missing however, this method will be faster. Alternatively, you could use a linked list for List2.
Note that this will fail if there are duplicate Key entries in List1. You'd need to check for that seperately to prevent multiple new items from being created in List2.
var lst1 = new List<int>() { 1, 2, 3, 4 };
var lst2 = new List<int>() { 1, 1, 2, 2, 2, 4 };
lst2.AddRange(lst1.Except(lst2));
LINQ: From the example you gave in the link, just change the code from:
foreach (var i in q) {
Console.WriteLine("Customer: {0} Order Number: {1}",
i.Name.PadRight(11, ' '), i.OrderNumber);
}
to
foreach (var i in q) {
if (i.OrderNumber == "(no orders)")
order.Add(new Order {Key = i.ID /* add your default values here*/});
}
Of course you can save some lines here as well in the code before.
OK, here is goes:
1. Create a type to represent an item from your lists:
struct ListType
{
public object Id;
public object Name;
}
or, of course you can build in another way that suits you better.
Create your List2 as an IEnumerable< ListType > from your LINQ query
I assume List1 has the same structure as List2 with an Id and Name field (you could use the same ListType type for the list items)
With the assumptions above, here is the code to solve the initial problem :
List newLst2 = list2.ToList();
Array.ForEach(list1.ToArray(), list1It =>
{
var isInList2 = from list2it in newLst2.ToArray()
where (string)list2it.Id == list1It.Id
select list2it;
if (isInList2.Count() == 0)
newLst2.Add(new ListType { Id = list1It.Id, Name = list1It.Name });
});
Comments: for each element in List1 make a query in List2 and check if the Id exists. If it does not exist, add a new item.
There probably are more efficient ways of doing this but this should be able to get you started.
Here is a solution using LINQ.
public class List1
{
public int ID { get; set; }
public string Person { get; set; }
}
public class List2
{
public int ID { get; set; }
public int Value { get; set; }
}
var lList1 = new List<List1>
{
new List1 {ID = 1, Person = "Alice"},
new List1 {ID = 2, Person = "Bob"},
new List1 {ID = 3, Person = "Carol"},
new List1 {ID = 4, Person = "Dave"}
};
var lList2 = new List<List2>
{
new List2 {ID = 1, Value = 15},
new List2 {ID = 1, Value = 19},
new List2 {ID = 2, Value = 5},
new List2 {ID = 2, Value = 7},
new List2 {ID = 2, Value = 20},
new List2 {ID = 4, Value = 16}
};
var lOutput = lList1.SelectMany(pArg =>
lList2.Where(pArg1 => pArg1.ID == pArg.ID)
.DefaultIfEmpty(new List2 { ID = pArg.ID, Value = 0})
.Select(pArg1 => pArg1));
Uh... It seems like it would be straightforward to just use Contains, no?
foreach (Key key in List1.Keys)
{
if (!List2.Keys.Contains(key)) List2.Add(key, "0");
}
This would have no issues with duplicate keys in List1.
LINQ implementation
public class Master
{
public int ID;
}
public class Detail
{
public int ID;
public int Count;
}
public static void AddMissingDetails(IEnumerable<Master> masters, List<Detail> details)
{
AddMissingDetails(masters, details, x => new Detail
{
ID = x,
Count = 0
});
}
public static void AddMissingDetails(IEnumerable<Master> masters, List<Detail> details, Func<int, Detail> createDefault)
{
details.AddRange(
masters
.Select(x => x.ID)
.Except(details.Select(x => x.ID).Distinct())
.Select(createDefault));
}
YOu may not like my solution. But i would like to thank you for this post.
It gave me a chance to do some useful stuff using linq.
I am using Extension method and Linq to add the missing items in your target list (List2).
I am not sure if you are working on 3.0/3.5 framework, if you do, then this solution would work for you and it is also "a clever, clean and efficient way of doing this" :).
public static void MergeLists() {
var listOne=new List<List1> {
new List1 {ID=1, Person="Alice"},
new List1 {ID=2, Person="Bob"},
new List1 {ID=3, Person="Carol"},
new List1 {ID=4, Person="Dave"},
new List1 {ID=5, Person="Dave2"},
new List1 {ID=6, Person="Dave3"},
};
var listTwo=new List<List2> {
new List2 {ID=1, Value=15},
new List2 {ID=1, Value=19},
new List2 {ID=2, Value=5},
new List2 {ID=2, Value=7},
new List2 {ID=2, Value=20},
new List2 {ID=4, Value=16}
};
var listTwoWithAddedItems=listOne.AddMissingItems(listTwo, (item1, item2) => item1.ID==item2.ID,
item2 => new List2 { ID=item2.ID, Value=-1 }).ToList();//For this value, you can put whatever default value you want to set for the missing items.
Console.Read();
}
public static class AmbyExtends {
public static List<Target> AddMissingItems<Source, Target>(this IEnumerable<Source> source, List<Target> target, Func<Source, Target, bool> selector, Func<Source, Target> creator) {
foreach(var item in source) {
if(!target.Any(x=>selector(item,x))) {
target.Add(creator(item));
}
}
return target;
}
}
INSERT INTO List2(ID, Value)
SELECT ID, 0
FROM List1
WHERE NOT EXISTS (SELECT NULL FROM List2 WHERE List2.ID=List1.ID)
Using SQL on the database, the solution would be:
INSERT INTO List2 (ID)
SELECT l1.ID
FROM List1 l1
LEFT JOIN List2 l2
ON l1.ID = l2.ID
WHERE l2.ID IS NULL
Here the assumption is that the other columns in List2 table are either NOT NULL or have a DEFAULT value constraint.

Categories