Compare one value to several possible list matches - c#

I'm back to haunt your dreams! I'm working on comparing some values in a complex loop. List 1 is a list of questions/answers, List 2 is also a list of questions/answers. I want to compare List 1 to List 2 and have duplicates removed from List 1 before merging it with List 2. My problem is in the current seed data I have the two items in List 1 match against List 2, but only one is removed instead of both.
I've been at this a couple days and my head is ready to explode, so I hope I can find some help!
Here's code for you:
//Fetching questions/answers which do not have an attempt
//Get questions, which automatically pull associated answers thanks to the model
List<QuizQuestions> notTriedQuestions = await db.QuizQuestions.Where(x=>x.QuizID == report.QuizHeader.QuizID).ToListAsync();
//Compare to existing attempt data and remove duplicate questions
int i = 0;
while(i < notTriedQuestions.Count)
{
var originalAnswersCount = notTriedQuestions.ElementAt(i).QuizAnswers.Count;
int j = 0;
while(j < originalAnswersCount)
{
var comparedID = notTriedQuestions.ElementAt(i).QuizAnswers.ElementAt(j).AnswerID;
if (report.QuizHeader.QuizQuestions.Any(item => item.QuizAnswers.Any(x => x.AnswerID == comparedID)))
{
notTriedQuestions.RemoveAt(i);
//Trip while value and cause break out of loop, otherwise you result in a catch
j = originalAnswersCount;
}
else
{
j++;
}
}
i++;
}
//Add filtered list to master list
foreach (var item in notTriedQuestions)
{
report.QuizQuestions.Add(item);
}

Try List.Union It is meant for exactly this sort of thing.

Related

Loop through list of strings to find different values

I have a list that is populated with different values:
e.g
{GBP, GBP, GBP, USD}
so far I have this:
List<string> currencyTypes = new List<string>();
for (int i = 0; i < currencyTypes.Count; i++)
{
if currencyTypes[i] != [i]
console.writeline("currencies are different");
}
So if the list has all the same entries, the if statement shouldnt fire
e,g {GBP, GBP, GBP, GBP}
however if any of the values are different from the rest then the if statement should notice the difference and fire.
this doesnt work however.
any ideas?
You could use LINQ to test whether all entries are the same
if (currencyTypes.Distinct().Count() > 1) {
Console.WriteLine("currencies are different");
}
Slightly more efficient for long lists:
if (currencyTypes.Count > 1 && currencyTypes.Distinct().Skip(1).Any()) {
Console.WriteLine("currencies are different");
}
This is more efficient because Any iterates at most one element unlike Count which iterates the whole list.
First of all, your list is empty. Maybe it's for the sake of the example. If not, initialize it with data. However, modify line 3 and 5 to this to fix the problem.
for (int i = 1; i < currencyTypes.Count; i++)
{
if (currencyTypes[i] != currencyTypes[i-1])
....
}
you should first group your data and find your result depending the group.
eg
List<string> currencyTypes = new List<string>() {"USD", "GBP", "GBP", "GBP" };
// group list items
var typeGroup = currencyTypes.GroupBy(t => t);
if (typeGroup.Count() > 1)
Console.WriteLine("currencies are different");
// .
// .
// also you can check what item is unique
foreach (var t in typeGroup.Where(g => g.Count() == 1 ))
{
Console.WriteLine($"{t.Single()} is different");
}

Get the count of values from a dictionary C#

I have a Dictionary which has an ID as the key and a list of lists as the value. I would like to get the count of how many lists are inside the list. This appears to give the correct value when I query it whilst debugging but when I go to try and access that data it only gives a count of 1 rather than 2. I'm sure it's something I'm missing but I can't put my finger on it.
Here's the count when I check it through debugging:
And here's it when I try to access that count of 2:
The whole method is:
public static List<string> getStatisticsCSVHeaders(List<Items> itemList, Dictionary<int, List<List<Statistic>>> availableStats)
{
List<string> topRow = new List<string>();
for (int i = 0; i < availableStats.Values.Count; i++)
{
topRow.Add("Phase " + (i+1));
for (int k = 0; k < itemList.Count; k++)
topRow.Add(getMetricHeader(itemList[k], true));
}
return topRow;
}
I'd like to have the number of lists inside my list as the counter for the line i < availableStats.Values.Count.
EDIT:
I should mention I've tried availableStats.Values[0].Count but this won't compile.
Debugger shows that you have a single item in your dictionary, and that item is a list with 2 elements. Because your code is taking the count of item in the dictionary you're getting 1.
To get the number of all the items in all the lists in that dictionary, try LINQ and Sum:
availableStats.Values.Sum(x => x.Count)
In your question, because value contains a list, so it is possible that it may contain a null value, so it is necessary to do a null check for values otherwise you can get an error inside your LINQ query.
var totalCount = availableStats.Values.Sum(x => x==null? 0 : x.Count);
There is one more way to get same result as follows:
var totalCount = availableStats.Sum(x => x.Value==null? 0 : x.Value.Count);

How to break from the loop when adding values to the list object which is distinct from already added values of the list?

i have one list object which contains int like below:
var list = new List<int>();
var ids=getting only ids from database.for Eg:1,2,1,3,5,6,3.
Now what i want to do is after fetching ids from datatabase i want to add in to my list object and if any differnt ids is added(eg:ids:2) then i want to break from the loop.
For Ex: After adding 1 in my list now if i try to add 2 which doesnt exist in my list object then i want to break from my loop with status different element found outside the loop.
Add values in the list object till duplicate value is found means add only duplicate values in my list object and if distinct value is found which is not already in the list then break from loop.Break from the loop after adding 1 in list because 2 is different and it is not already in the list object.
This is my code:
bool status;
for (var i = 0; i < ids.Count; i++)
{
list.Add(ids);
}
//here i want status with different element found or not
Note:I just want to add only duplicates values in my list object until new id is found.
i just want to add duplicate values in list object until different ids is found.
var list = new List<int>{ 1, 2, 3, 4};
var idsFromDb = new List<int> {1, 2, 5, 3};
foreach (int id in idsFromDb)
{
if (list.Contains(id))
{
break;
}
list.Add(id);
}
You can use foreach to iterate over elements of ids
break works in foreach just the way it does in for
if we don't break, do whatever you want to do, for example, add the value to list
Contains checks if list already contains id.
This makes it roughly an O(n^2) operation - because both foreach and Contains iterate over elements of list
Code:
foreach (var id in ids)
{
if (list.Contains(id))
{
break;
}
list.Add(id);
}
You can use Contains
status = false;
for (var i = 0; i < ids.Count; i++)
{
if (list.Count > 0 && !list.Contains(ids[i])){
list.Add(ids[i]); //add this, be it duplicate or not
status = true; //different element found
break; //break if this is not duplicate
} else {
//do something for duplicate
list.Add(ids[i]); //add this, be it duplicate or not
}
}
Contains can check if your item already exists in the List
Here's your desired HashSet<T> + LINQ approach:
var dupChecker = new HashSet<int> {ids[0]}; // add first element, now only duplicates are allowed
List<int> dupList = ids.TakeWhile(i => !dupChecker.Add(i)).ToList();

take one value from multiple loops

I have a list of arrays, of which i want to take one value from each array and build up a JSON structure. Currently for every managedstrategy the currency is always the last value in the loop. How can i take the 1st, then 2nd value etc while looping the names?
List<managedstrategy> Records = new List<managedstrategy>();
int idcnt = 0;
foreach (var name in results[0])
{
managedstrategy ms = new managedstrategy();
ms.Id = idcnt++;
ms.Name = name.ToString();
foreach (var currency in results[1]) {
ms.Currency = currency.ToString();
}
Records.Add(ms);
}
var Items = new
{
total = results.Count(),
Records
};
return Json(Items, JsonRequestBehavior.AllowGet);
JSON structure is {Records:[{name: blah, currency: gbp}]}
Assuming that I understand the problem correctly, you may want to look into the Zip method provided by Linq. It's used to "zip" together two different lists, similar to how a zipper works.
A related question can be found here.
Currently, you are nesting the second loop in the first, resulting in it always returning the last currency, you have to put it all in one big for-loop for it to do what you want:
for (int i = 0; i < someNumber; i++)
{
// some code
ms.Name = results[0][i].ToString();
ms.Currency = results[1][i].ToString();
}

Concurent foreach iteration of two list strings

Let's say I have two List<string>. These are populated from the results of reading a text file
List owner contains:
cross
jhill
bbroms
List assignee contains:
Chris Cross
Jack Hill
Bryan Broms
During the read from a SQL source (the SQL statement contains a join)... I would perform
if(sqlReader["projects.owner"] == "something in owner list" || sqlReader["assign.assignee"] == "something in assignee list")
{
// add this projects information to the primary results LIST
list_by_owner.Add(sqlReader["projects.owner"],sqlReader["projects.project_date_created"],sqlReader["projects.project_name"],sqlReader["projects.project_status"]);
// if the assignee is not null, add also to the secondary results LIST
// logic to determine if assign.assignee is null goes here
list_by_assignee.Add(sqlReader["assign.assignee"],sqlReader["projects.owner"],sqlReader["projects.project_date_created"],sqlReader["projects.project_name"],sqlReader["projects.project_status"]);
}
I do not want to end up using nested foreach.
The FOR loop would probably suffice. Someone had mentioned ZIP to me but wasn't sure if that would be a preferable route to go in my situation.
One loop to iterate through both lists (assuming both have same count):
for (int i = 0; i < alpha.Count; i++)
{
var itemAlpha = alpha[i] // <= your object of list alpha
var itemBeta = beta[i] // <= your object of list beta
//write your code here
}
From what you describe, you don't need to iterate at all.
This is what you need:
http://msdn.microsoft.com/en-us/library/bhkz42b3.aspx
Usage:
if ((listAlpga.contains(resultA) || (listBeta.contains(resultA)) {
// do your operation
}
List Iteration will happen implicitly inside the contains method. And thats 2n comparisions, vs n*n for nested iteration.
You would be better off with sequential iteration in each list one after the other, if at all you need to go that route.
This list is maybe better represented as a List<KeyValuePair<string, string>> which would pair the two list values together in a single list.
There are several options for this. The least "painful" would be plain old for loop:
for (var index = 0; index < alpha.Count; index++)
{
var alphaItem = alpha[index];
var betaItem = beta[index];
// Do something.
}
Another interesting approach is using the indexed LINQ methods (but you need to remember they get evaluated lazily, you have to consume the resulting enumerable), for example:
alpha.Select((alphaItem, index) =>
{
var betaItem = beta[index];
// Do something
})
Or you can enumerate both collection if you use the enumerator directly:
using (var alphaEnumerator = alpha.GetEnumerator())
using (var betaEnumerator = beta.GetEnumerator())
{
while (alphaEnumerator.MoveNext() && betaEnumerator.MoveNext())
{
var alphaItem = alphaEnumerator.Current;
var betaItem = betaEnumerator.Current;
// Do something
}
}
Zip (if you need pairs) or Concat (if you need combined list) are possible options to iterate 2 lists at the same time.
I like doing something like this to enumerate over parallel lists:
int alphaCount = alpha.Count ;
int betaCount = beta.Count ;
int i = 0 ;
while ( i < alphaCount && i < betaCount )
{
var a = alpha[i] ;
bar b = beta[i] ;
// handle matched alpha/beta pairs
++i ;
}
while ( i < alphaCount )
{
var a = alpha[i] ;
// handle unmatched alphas
++i ;
}
while ( i < betaCount )
{
var b = beta[i] ;
// handle unmatched betas
++i ;
}

Categories

Resources