I have a list of items that can each have multiple keywords so I have three tables
Item -> ItemKeyword <- Keyword
I want to return all Items where the Item has all keywords in a list. so for example:
Item 1 has keywords "Rabbit", "Dog", "Cat"
Item 2 has keywords "Rabbit", Hedgehog", "Dog"
I want to return only those items that have both "Dog" and "Cat" as keywords.
I cant use a contains query as that will essentially return all those items with "Dog" OR "Cat".
So I guess what I am asking for is if there is such a thing called ContainsAll in linq, or if there is some way I can perform this functionality.
I have attempted to use Except and Intersect but I cant seem to get the correct results.
I would like to have a single query so that it is easy to compile the query but this is not a dealbreaker.
I am using:
Dot Net 4.5
Visual Studio 2012
C#
Linq
Entity Framework
Sql Server Ce 4.0
I cant use a contains query as that will essentially return all those items with "Dog" OR "Cat".
This is simply not true. You can use two contains with an AND && :
items.Where(item => item.Keywords.Contains("Dog") && item.Keywords.Contains("Cat"));
Or you can put the values you are looking for in an array then use All method to make it shorter:
var values = new [] { "Dog", "Cat" };
items.Where(item => values.All(item.Keywords.Contains));
Please check this .. code is written lengthier for better understanding .. Assuming each item as an identifier to check
List<item> ItemsList = new List<item>();
item item1 = new item();
item1.ID = "1";
item1.keywords = new List<string>();
item1.keywords.Add("Rabbit");
item1.keywords.Add("Dog");
item1.keywords.Add("Cat");
ItemsList.Add(item1);
item item2 = new item();
item2.ID = "2";
item2.keywords = new List<string>();
item2.keywords.Add("Rabbit");
item2.keywords.Add("Hedgehog");
item2.keywords.Add("Dog");
ItemsList.Add(item2);
//this the list you want to check
var values = new List<string> ();
values.Add("Dog");
values.Add("Cat");
var result = from x in ItemsList
where !(values.Except(x.keywords).Any())
select x;
foreach (var item in result)
{
// Check the item.ID;
}
Related
I have a master list that has the values for tables of a database I know to be correct:
masterList: List<string>(){ "business", "customer", "location", "employee", etc}
And I've queried a new database that is supposed to be identical. My test will tell me if I have any errors in the scripts my team has made to make this new DB. tablesList is supposed to the be the return of my query:
tablesList: List<string>(){ "business", "customer", "location", "employee", etc}
So in practice they are supposed to be the same, but to test errors, I want to compare the tablesList against the masterList to make sure all needed tables are there. As a copy of this process, I'm also reversing the search, in case there are any extra tables that are not there on the masterList.
Question: How do I compare a list against a master list, and return items that don't match up?
I am using Visual Studio 2017 with c# .net Core 2.0.
Here is what I've been trying so far:
var errorsList = new List<string>();
tablesList = QuerySchemaForTables();
masterList = GrabMasterTableList();
foreach(var item in masterList)
errorsList.Add(tablesList.Where(x => x.Contains(item)));
But with this, I'm getting the error:
cannot convert from IEnumerable to string
You can get the two directions of errors using LINQ. No need for the loop:
var missingInMasterList = tableList.Where(x => !masterList.Contains(x)).ToList();
var missingInTableList = masterList.Where(x => !tableList.Contains(x)).ToList();
Are you looking for something like that;
var errorList = tableList.Where(x => !masterList.Contains(x));
You can capture the differences using .Except(), which is one of the IEnumerable set operations:
var missingTables = masterList.Except(tablesList);
var extraTables = tablesList.Except(masterList);
Then, to create your error message, you can join the items in these IEnumerables with a comma into a single string using string.Join():
var errorMessage = new StringBuilder();
if (missingTables.Any())
{
errorMessage.AppendLine("Missing Tables: " + string.Join(", ", missingTables));
}
if (extraTables.Any())
{
errorMessage.AppendLine("Extra Tables: " + string.Join(", ", extraTables));
}
Then you can output your results by checking the length of errorMessage to determine if any errors were encountered:
if (errorMessage.Length > 0)
{
Console.WriteLine(errorMessage.ToString());
}
else
{
Console.WriteLine("No extra or missing tables detected");
}
I think better to use is Except() as follows
var MasterList = new List<string> { "business", "customer", "location", "employee"};
var ChildList = new List<String> { "customer", "location", "employee" };
var filter = MasterList.Except(ChildList);
This will values those are not in ChildList.You can also do vice versa.
To find all items that are in the tablesList but not in the masterList use .Contains:
var errorsList = tableList.Where(x => !masterList.Contains(x));
But I recommend you use a HashSet<String> for masterList so search for an item in it will be in O(1) instead of O(n):
var masterCollection = new HashSet<String>(GrabMasterTableList());
var errorsList = tableList.Where(x => !masterCollection.Contains(x));
As for the problem with your code as you posted:
foreach(var item in masterList)
errorsList.Add(tablesList.Where(x => x.Contains(item))); // <-- error
As the error points out, Linq's .Where returns an IEnumerable<T> whereas .Add expects a single item of the type of the collection, which in this case is a single string. You could use .AddRange instead but I think a better use all together is what I wrote above.
Your code is presently trying to add an IEnumerable to a List.
If you want to add all the matches you should AddRange instead.
https://msdn.microsoft.com/en-us/library/z883w3dc(v=vs.110).aspx
var errorsList = new List<string>();
tablesList = QuerySchemaForTables();
masterList = GrabMasterTableList();
foreach(var item in masterList)
errorsList.AddRange(tablesList.Where(x => x.Contains(item)));
I'd like to loop over a string list, and find out if the items from this list start with one of the item from another list.
So I have something like:
List<string> firstList = new List<string>();
firstList.Add("txt random");
firstList.Add("text ok");
List<string> keyWords = new List<string>();
keyWords.Add("txt");
keyWords.Add("Text");
You can do that using a couple simple for each loops.
foreach (var t in firstList) {
foreach (var u in keyWords) {
if (t.StartsWith(u) {
// Do something here.
}
}
}
If you just want a list and you'd rather not use query expressions (I don't like them myself; they just don't look like real code to me)
var matches = firstList.Where(fl => keyWords.Any(kw => fl.StartsWith(kw)));
from item in firstList
from word in keyWords
where item.StartsWith(word)
select item
Try this one it is working fine.
var result = firstList.Where(x => keyWords.Any(y => x.StartsWith(y)));
I am trying to trim the value/text combos of a IEnumerable<SelectListItem> item in C#. I am able to do this with the code below but was wondering if it could be accomplished with Linq?
IEnumerable<SelectListItem> list = //function that fills the IEnumerable<SelectListItem>;
foreach (SelectListItem item in list)
{
item.Value = item.Value.Trim();
item.Text = item.Text.Trim();
}
LINQ is designed to define queries. What you have there is not a query; it is modifying/mutating items. As such, LINQ is not an effective tool to accomplish that, a foreach loop is (your implementation of one is just fine). If you wanted to use LINQ it would be to create entirely new items, rather than modifying existing items. That may or may not be what you really wanted to do. If it is, then it would be:
list.Select(item => new SelectListItem()
{
Value = item.Value.Trim(),
Text = item.Text.Trim(),
});
var result = list.Select(x => new SelectListItem {
Value = x.Value.Trim(),
Text = x.Text.Trim()
});
I believe this should do the trick. Why didn't yours work, by the way?
i have two lists having few elements in common, i want to remove duplicates events except few as described below..and the order of the string must be same and both list may not contain same no of elements?
list A: List B
ASCB ASCB
test1 test1
test2 test5
test3 test3
test4 test6
Arinc Arinc
testA testC
testB testB
testC
tesctD
now i want to remove all common elements in two list except elements ASCB, ARINC.. how to do that can any one help me in that...
I would just store the special values ( ASCB, ARINC, ect ) in their own list so I can use Except to get the difference between the two sets. You can add the special values in afterwards.
List<string> except = ListA.Except(ListB).Concat(listB.Except(ListA)).Concat(SpecialValues).ToList();
You have to call except twice because first we get items in A that are not in B. Then we add items that are in B but not in A. Finally we add the special values (I'm assuming SpecialValues is a collection with the strings you don't want removed).
You'd have to test performance as I suspect it's not the most efficient.
List<string> wordstoKeep = new List<string>() { "ASCB", "Arinc" };
foreach (string str in listB)
{
int index = listA.FindIndex(x => x.Equals(str, StringComparison.OrdinalIgnoreCase));
if (index >= 0)
{
if (!wordstoKeep.Any(x => x.Equals(str, StringComparison.OrdinalIgnoreCase)))
listA.RemoveAt(index);
}
else
listA.Add(str);
}
var listA = new List<string>{"ASCB","test1","test2"};
var listB = new List<string>{"ASCB","test1","test2"};
var combinedList = listA.Where(a => a.Contains("test"))
.Concat(listB.Where(b => b.Contains("test")))
.Distinct().Dump();
outputs 'test1', 'test2'
your filter conditions are contained in your Where clause.
Where can be whatever condition you want to filter by:
Where(a => a != "ASCB" or whatever...
Concat joins the two lists. Then call Distinct() to get unique entries.
Going off the requirement that order must be the same
if(B.Count != A.Count)
return;
List<String> reserved = new List<string>{ "ARCB", "ARINC" };
for (int i = A.Count -1; i >= 0; i--)
{
if (!reserved.Contains(A[i].ToUpper()) && A[i] == B[i])
{
A.RemoveAt(i);
B.RemoveAt(i);
}
}
This works:
var listA = new List<string>()
{
"ASCB",
"test1",
"test2",
"test3",
"test4",
"Arinc",
"testA",
"testB"
};
var listB = new List<string>()
{
"ASCB",
"test1",
"test5",
"test3",
"test6",
"Arinc",
"testC",
"testB"
};
var dontRemoveThese = new List<string>(){"ASCB", "Arinc"};
var listToRemove = new List<string>();
foreach (var str in listA)
if (listB.Contains(str))
listToRemove.Add(str);
foreach (var str in listToRemove){
if (dontRemoveThese.contains(str))
continue;
listA.Remove(str);
listB.Remove(str);
}
I like this solution because you can see what happens. I'd rather have 10 lines of code where it's obvious what happens than 1-3 lines of obscure magic.
I have the following code.
MyDataContext db = MyDataContext.Create();
bc =
db.BenefitCodes.Select(
b =>
new
{
BenCd = b.BenCd
, Description = b.BenDesc
, BenInterest = b.BenInterest
, CodeDescription = string.Format("{0} - {1}", b.BenCd, b.BenDesc)
});
I had to go the Anonymous type route as CodeDescription isn't a property of benefitCode and the customer wants it to appear this way in a dropDrownList. Anyways my question is how can I select a subset of items from this list? I need to select items based on the BenInterest attribute.
So this returns IEnumerable, so I am trying to go this route and this is where I get stuck. My intent is to build a new IEnumerable list and set a dropdown datasource to it.
IEnumerator enumerator = BenefitCodes.GetEnumerator();
while(enumerator.MoveNext())
{
//What can I do here to return items based on BenInterest?
//I basically either want items that have a BenInterest of 'E'
// or items that DO NOT have a BenInterest of 'E'
// this is based on the value of a radioButtonList on the page
}
So how do I create a new Enumerable of the same Anonymous type that only contains the desired items.
Thanks for any help.
Cheers,
~ck
You can just use:
var newCollection = bc.Where( e => e.BenInterest == 'E' );