I need to select random questions per category
private int[] categoryId = {1, 2, 3, 4, 5, ...};
private int[] questionsPerCategory = {3, 1, 6, 11, 7, ...};
Before linq i achieved it by using
SELECT TOP (#questionsPerCategory) * From Questions WHERE CategoriesID = #categoryId AND
InTest ='1' ORDER BY NEWID()
Which also was not correct, since i had to call this for every categoryId.
How can i have the desired results with linq in a single query?
All i need is fetch
3 random questions, with categoryId = 1 and InTest = true,
1 random question, with categoryId = 2 and InTest = true,
6 random questions, with categoryId = 3 and InTest = true
and so on..
Since Guid.NewGuid is not supported by LINQ to SQL, first you need to get access to NEWID function by using the trick from the accepted answer to Random row from Linq to Sql by adding the following to your context class:
partial class YourDataContext {
[Function(Name="NEWID", IsComposable=true)]
public Guid Random()
{ // to prove not used by our C# code...
throw new NotImplementedException();
}
}
Then the query for single CategoryID and question count would be:
var query = db.Questions
.Where(e => e.CategoriesID == categoryId[i] && e.InTest)
.OrderBy(e => db.Random())
.Take(questionsPerCategory[i])
To get the desired result for all category / question count pairs, you could build a UNION ALL SQL query by using Concat of the above single query for i = 0..N like this:
var query = categoryId.Zip(questionsPerCategory,
(catId, questions) => db.Questions
.Where(q => q.CategoriesID == catId && q.InTest)
.OrderBy(q => db.Random())
.Take(questions)
).Aggregate(Queryable.Concat)
.ToList();
This should produce the desired result with single SQL query. Of course it's applicable if the count of the categoryId is relative small.
Maybe you want something like this, you do a group by then select how many you want from each category.
Edited: As pointed out by Enigmativity in the comments, Guid.NewGuid() shouldn't be used to for randomness only for uniqueness. To produce randomness you should consult this StackOverflow post.
Demo
using System;
using System.Linq;
using System.Collections.Generic;
public class Program
{
private static int[] categoryIds = new int[] {1, 2, 3, 4, 5};
private static int[] questionsPerCategory = {3, 1, 6, 11, 7};
//Part of demo
private static IEnumerable<QuestionVM> Questions = Enumerable.Range(0,100).Select(x=> new QuestionVM { Question = $"Question - {x}", CategoryId = (x % 5) + 1});
public static void Main()
{
var questions = Questions.Where(x=> x.InTest).GroupBy(x=> x.CategoryId).SelectMany(x=> x.OrderBy(y=> Guid.NewGuid()).Take(GetQuestionTake(x.Key)));
foreach(var question in questions)
Console.WriteLine($"{question.Question} - CategoryId: {question.CategoryId}");
}
///Finds out how many questions it should take by doing a search and then picking the element in the same position
private static int GetQuestionTake(int categoryId)
{
int element = categoryIds.Select((x, i) => new { i, x }).FirstOrDefault(x => x.x == categoryId).i;
return questionsPerCategory.ElementAtOrDefault(element);
}
}
//Part of demo
public class QuestionVM
{
public string Question {get;set;}
public int CategoryId {get;set;}
public bool InTest {get;set;} = true;
}
I think you are looking for Take() method.
You should also pass parameters to the method with category id and how many questions you want to receive. Pass those parameters from your arrays.
private IQuerable<Question> Method(int Id, int questionsCount)
{
return Questions.Where(c=>c.CategoriesId==Id && c.InTest==1).Take(questionsCount).OrderBy(c=>c.NewId);
}
A common way is to order by Guid.NewGuid(), so to extend Crekate's answer above.
.OrderBy(c=>Guid.NewGuid());
Related
I have a list that is being populated with the summed quantities of specific product ID's, for example:
My table:
My program checks for a user input and then compares that user input to the Quantity column in the table - once a match is found, the program checks for the ProductID of the matched quantity, and then adds ALL the quantities relating to that matched ProductID.
For example, if a user enters 2, the program will check for all the values in the Quantity column greater than or equal to 2 - so in this case, the first row to be found would be row 2 (which has a productID of 9 and a quantity of 2). The program should now search for ALL productID's equal to 9 and then sum their individual quantities (which will be 3 in this case, as the ProductID 9 appears only twice, with the quantities 2 and 1 respectively), and then save that final summed quantity (3) into the list. And then the program should continue to row 3 (which has a productID of 8 and a quantity of 3). The program should now search for ALL productID's equal to 8 and then sum their individual quantities (which will be 9 in this case, as the ProductID 8 appears three times, with the quantities 3 and 3 and 3 respectively), and then save that final summed quantity (9) into the list. And then the program should SKIP row 4, because the ProductID of 9 has already been dealt with - and so on and so forth.
So far i have tried this:
var dynamicReader = DBAccessor.InvoiceLines.Where(xx => xx.Quantity >= quantitySelected).Select(yy => yy.ProductID);
foreach (var product in dynamicReader)
{
if (!quantityArrayList.Contains(product))
{
quantityArrayList.Add(DBAccessor.InvoiceLines.Where(gg => gg.ProductID == product).Sum(g => g.Quantity));
}
}
If the user enters 2, the first 4 results using this method are correct, however, after that, the summed values fetched make little sense to me. Any help on this matter would be greatly appreciated.
It looks like you're not associating the ProductID with the Quantity stored in quantityArrayList. So, when you check quantityArrayList.Contains(product) you're not checking to see if that ProductID has already been processed, you're checking to see if there is a sum of quantities which match the ProductID.
I don't know what type quantityArrayList is, I'm assuming it's a List<int>. Instead, you should use a Dictionary where the key would be the ProductID and the value would be the sum of the Quantity.
Untested, but the updated code might look like...
Dictionary<int, int> productQuantities = new Dictionary<int, int>(); //I've changed the name, but this would replace quantityArrayList
var dynamicReader = DBAccessor.InvoiceLines.Where(xx => xx.Quantity >= quantitySelected).Select(yy => yy.ProductID);
foreach (var product in dynamicReader)
{
if (!productQuantities.ContainsKey(product))
{
productQuantities.Add(product, DBAccessor.InvoiceLines.Where(gg => gg.ProductID == product).Sum(g => g.Quantity));
}
}
I would use GroupBy with 2 values mapped to the grouping: Max and Sum. If the max quantity of one of the entries is greater than your desired amount, that grouping is 'in scope' and you want to get its sum. Like this:
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
var productCounts = new List<ProductCount>{
new ProductCount{ProductId = 10, Quantity = 1},
new ProductCount{ProductId = 9, Quantity = 2},
new ProductCount{ProductId = 8, Quantity = 3},
new ProductCount{ProductId = 9, Quantity = 1},
new ProductCount{ProductId = 7, Quantity = 2},
new ProductCount{ProductId = 5, Quantity = 3},
new ProductCount{ProductId = 8, Quantity = 3},
new ProductCount{ProductId = 6, Quantity = 2}
};
var desiredMinimum = 3;
var output = productCounts.GroupBy(i => i.ProductId)
.Select(i => new
{
ProductId = i.Key,
Max = i.Max(j => j.Quantity),
Total = i.Sum(j => j.Quantity)
})
.Where(i => i.Max >= desiredMinimum)
.ToList();
foreach(var outputItem in output){
Console.WriteLine("ProductId: " + outputItem.ProductId + "; Total: " + outputItem.Total);
}
}
internal class ProductCount
{
public int ProductId{get;set;}
public int Quantity{get;set;}
}
}
See:
https://dotnetfiddle.net/HmMc4h
This is not a duplicate of: Given 2 C# Lists how to merge them and get only the non duplicated elements from both lists since he's looking at lists of the same type.
I have this scenario:
class A
{
string id;
.... some other stuff
}
class B
{
string id;
.... some other stuff
}
I would like to remove, both from A and B, elements that share an id field between the two lists.
I can do it in 3 steps: find the common ids, and then delete the records from both lists, but I'm wondering if there is something more elegant.
Edit: expected output
var A = [ 1, 3, 5, 7, 9 ]
var B = [ 1, 2, 3, 4, 5 ]
output:
A = [ 7, 9 ]
B = [ 2, 4 ]
but this is showing only the id field; as stated above, the lists are of different types, they just share ids.
You will require three steps, but you can use Linq to simplify the code.
Given two classes which have a property of the same (equatable) type, named "ID":
class Test1
{
public string ID { get; set; }
}
class Test2
{
public string ID { get; set; }
}
Then you can find the duplicates and remove them from both lists like so:
var dups =
(from item1 in list1
join item2 in list2 on item1.ID equals item2.ID
select item1.ID)
.ToArray();
list1.RemoveAll(item => dups.Contains(item.ID));
list2.RemoveAll(item => dups.Contains(item.ID));
But that is still three steps.
See .Net Fiddle example for a runnable example.
You can use LINQ Lambda expression for elegance:
var intersectValues = list2.Select(r => r.Id).Intersect(list1.Select(r => r.Id)).ToList();
list1.RemoveAll(r => intersectValues.Contains(r.Id));
list2.RemoveAll(r => intersectValues.Contains(r.Id));
Building on #Matthew Watson's answer you can move all of it to a single LINQ expression with
(from item1 in list1
join item2 in list2 on item1.ID equals item2.ID
select item1.ID)
.ToList()
.ForEach(d =>
{
list1.RemoveAll(i1 => d == i1.ID);
list2.RemoveAll(i2 => d == i2.ID);
}
);
I don't know where you land on the performance scale. The compiler might actually split this up into the three steps steps you already mentioned.
You also lose some readability as the from ... select result does not have a 'speaking' name like duplicates, to directly tell you what you will be working with in the ForEach.
Complete code example at https://gist.github.com/msdeibel/d2f8a97b754cca85fe4bcac130851597
O(n)
var aHash = list<A>.ToHashSet(x=>x.ID);
var bHash = list<B>.ToHashSet(x=>x.ID);
var result1 = new List<A>(A.Count);
var result2 = new List<B>(B.Count);
int value;
foreach (A item in list<A>)
{
if (!bHash.TryGetValue(item.ID, out value))
result1.Add(A);
}
foreach (B item in list<B>)
{
if (!aHash.TryGetValue(item.ID, out value))
result2.Add(B);
}
I have a problem I need to solve in the best algorithm I can find.
Let me describe the problem first.
I have a class A with number of Hashset<int> with Z number of items
A -> {x,y,z | x = {0,1,2} , y = {-1,0,9} ... }
B -> {x,y,z,k | x = {0,1,-2} , y = {-1,0,19} ... }
...
with an input of a new array of int { ... } entered by the user, the result should be the group with the most hashset with matching numbers between the input and the groups.
For example :
A : {[1,2,3][2,3,8][-1,-2,2]}
B : {[0,-9,3][12,23,68][-11,-2,2]}
Input :
[2,3,-19]
result A : {[2,3][2,3][2]}
result B : {[3][][2]}
A : 3
B : 2
A is the correct answer.
Or something like that .
Yes, I know it's a subjective question but it's for a good cause.
Assuming you have an unknown number of samples to check on the input set, this Linq query should do the trick.
from sample in samples
let intersectedSets =
from set in sample
let intersection = input.Intersect(set)
where intersection.Count() > 0
select intersection
orderby intersectedSets.Count() descending
select intersectedSets;
The top-most element is your desired sample, thus yourCollection.First() will yield your result set - In your given example:
var samples = new[] {
new[]{
new[]{1, 2, 3},
new[]{2, 3, 8},
new[]{-1, -2, 2}
},
new[]{
new[]{0, -9, 3},
new[]{12, 23, 68},
new[]{-11, -2, 2}
}
};
var input = new[]{2, 3, -19};
var result =
(from sample in samples
let intersectedSets =
from set in sample
let intersection = input.Intersect(set)
where intersection.Count() > 0
select intersection
orderby intersectedSets.Count() descending
select intersectedSets).First();
result.Dump(); // LINQPad extension method
apparently you want to use C# to implement this. I don't know if this is the best algorithm (in whatever context) but you could use LINQ to write it down very plain and simple:
int[][] arrays = new[] { new[] { 1, 2 }, new[] { 2, 3 }, new[] {3, 4} };
int[] input = new[] { 1, 4 };
Console.WriteLine(arrays.Count((itemarray) => itemarray.Any((item) => input.Contains(item))));
in an array of int arrays this finds the number of arrays that have at least one of the values of the input array. this is what you're doing, though I'm not sure if it's what you're asking of us.
Given a sample class HashHolder and an instance A of it:
public class HashHolder
{
public HashHolder()
{
Hashes = new List<HashSet<int>>();
}
public List<HashSet<int>> Hashes { get; set; }
}
You can group by hashset and take the maximum count between all groups:
var maxHash = A.Hashes.GroupBy(h => h)
.Select(g => new { Hash = g.Key, Count = input.Count(num => g.Key.Contains(num)) })
.OrderByDescending(g => g.Count)
.FirstOrDefault();
The result will then be maxHash.Hash if maxhHash is not null.
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.
Single and multiple lists
Consider the following lists:
List<Int32> appleIdentities = new List<int>(new[] { 1, 2, 3 });
List<Int32> chocolateIdentities = new List<int>(new[] { 2, 3, 4 });
List<Int32> icecreamIdentities = new List<int>(new[] { 11, 14, 15, 16 });
Using LINQ to SQL; is it possible to wite a statement which translates into:
SELECT
DesertsID,
DesertsName
FROM
Deserts
WHERE
Deserts.AppleIdentity IN (1, 2, 3) AND
Deserts.ChocolateIdentity IN (2, 3, 4) AND
Deserts.IcecreamIdentity IN (11, 14, 15m 16)
If yes; how would the code look if I wanted to query my database of deserts against just the appleIdentities list?
Arrays
Consider the following arrays:
Int32[] appleIdentities = new[] {1, 2, 3, 4};
String[] chocolateNames = new[] {"Light", "Dark"};
Using LINQ to SQL; is it possible to wite a statement which translates into:
SELECT
DesertsID,
DesertsName
FROM
Deserts
WHERE
Deserts.AppleIdentity IN (1, 2, 3) AND
Deserts.ChocolateName IN ('Light', 'Dark')
If yes; how would the code look if I wanted to query my database of deserts against just the appleIdentities array?
List of objects
Consider the following:
public class Identities
{
public Int32 appleIdentity { get; set; }
public String chokolateName { get; set; }
}
List<Identities> identities = new List<Identities>(new[] {
new Identities { appleIdentity = 1, chokolateName = "Light" },
new Identities { appleIdentity = 2, chokolateName = "Dark" },
});
Using LINQ to SQL; is it possible to wite a statement which translates into:
SELECT
DesertsID,
DesertsName
FROM
Deserts
WHERE
Deserts.AppleIdentity IN (1, 2) AND
Deserts.ChocolateName IN ('Light', 'Dark')
If yes; how would the code look if I wanted to query my database of deserts against just the appleIdentity-property on my list of Identities objects?
This is branch off of LINQ to SQL query against a list of entities
how would the code look if I wanted to
query my database of deserts against
just the appleIdentities list?
You can compose a linq query in multiple statements, like so, and select at runtime which filters your want to use in your where clause.
var query = db.Desserts;
if (filterbyAppleIdentity)
query = query.Where( q => appleIdentities.Contains(q.DesertsID));
if (filterbyChocolateIdentities)
query = query.Where( q => chocolateIdentities.Contains(q.DesertsID));
if (filterbicecreamIdentities)
query = query.Where( q => icecreamIdentities.Contains(q.DesertsID));
var deserts = query.ToList();
you can also write an extension method to do this without if statements: (Edit fixed typo, return type should be IQueriable
public static class LinqExtensions {
public IQueriable<T> CondWhere<T>(this IQueriable<T> query, bool condition, Expression<Func<T,bool>> predicate) {
if (condition)
return query.Where(predicate);
else
return query;
}
}
and write your linq query like this:
var deserts = db.Desserts;
.CondWhere(filterbyAppleIdentity, q => appleIdentities.Contains(q.DesertsID));
.CondWhere(filterbyChocolateIdentities, q => chocolateIdentities.Contains(q.DesertsID));
.CondWhere(filterbicecreamIdentities, q => icecreamIdentities.Contains(q.DesertsID)).ToList();
Another way to do it is to union the id lists:
var deserts = db.Deserts
.Where( d => appleIdentities.Union(chocolateIdentities).Union(icecreamIdentities).Contains(d.DesertsID);
For a list of objects you can use .Select extension method to project your list into a int or string IEnumerable and you can use contains in the query in the same way:
var deserts = db.Deserts
.Where(d =>
identities.Select(i => i.appleIdentity).Contains(d => d.DesertID) &&
identities.Select(i => i.chokolateName).Contains(d => d.DesertsName)
)
Sure - just use Contains - using Northwind as an example:
var qry = from cust in ctx.Customers
where custIds.Contains(cust.CustomerID)
&& regions.Contains(cust.Region)
select cust; // or your custom projection
Well, you can try:
var query = from dessert in db.Desserts
where appleIdentities.Contains(dessert.AppleIdentity)
&& chocolateIdentities.Contains(dessert.ChocolateIdentity)
&& iceCreamIdentities.Contains(dessert.IceCreamIdentity)
select new { dessert.Id, dessert.Name };
I believe that's okay, although it'll fail when the lists get big enough IIRC. That should be okay for lists and arrays.
I'm not sure about your third query though - I think you'd need a list for each of the separate Contains calls.
As others have said, LinqToSql will translate Contains to IN.
There's some caveats:
this translation works for List<T>.Contains(), but doesn't work for IList<T>.Contains(). Does it work for arrays? I don't know.
This translation will happily translate as many elements as you like - each element becomes a sql parameter. SQL Server 2008 has an approx 2000 parameter limit and will throw sql exceptions at you if you try this with a collection that is too big.
This translation, when applied to a collection of strings, will produce nvarchar parameters. This could be a serious problem if the target column is varchar and you want to use the index on this column. Sql Server will convert the index, instead of the parameters... which involves reading and converting every string in the whole index.
Here's some code for your List of Objects question:
List<int> someIDs = identities
.Select(x => x.appleIdentity).ToList();
List<string> someStrings = identities
.Select(x => x.chokolateName).ToList();
var query = db.Desserts.Where(d =>
someIDs.Contains(d.AppleIdentity) &&
someStrings.Contains(d.ChocolateName)
)