in C# I want to compare two Lists - c#

I have three lists
List1 < Labels > lbl and List2 < Strings > value and List3 < Strings > result
and I wanna compare both using foreach like
if (label1.text == value ) { // value is the 2nd list name
Label_Result.text = Result // in third List
Label1.font= new font("Tahoma",18);
ListBox1.items.add(Label.text);
}
Edit ,,
I think what I need is three Lists

Three basic examples. The first uses a simple nested foreach:
foreach(var item1 in list1)
foreach(var item2 in list2)
if(item1.text == item2)
{
//Do your thing
{
You could reduce nesting by using LINQ. Note that you could make this considerably fancier in LINQ (you can join lists), but I've chosen a simpelr example to show you the basic idea.
foreach(var item1 in list1)
{
var matchesInList2 = list2.Where(item2 => item1.text == item2);
foreach(var match in matchesInList2)
{
//Do your thing
}
}
There is a simpler way to approach it:
var matches = list1.Where(item1 => list2.Contains(item1.text));
foreach(var item1 in matches)
{
//Do your thing, e.g.:
//var theTextValue = item1.text;
}
To explain this simpler approach: we immediately filter list1, and only keep the elements whose .text value exists in list2.
After that, it is simply a matter of looping over the found matches, you don't need to filter anymore.

i think the best way is:
foreach(var list1item in lbl) //List1 item
{
foreach(var list2item in value) //List2 item
{
if(list1item == list2item)
{
//Do something
}
}
}

Related

A function that gets two variables and their order doesn't matter

Sorry that I couldn't explain better in the question header. I'll try to define my question better here.
That's what I am doing - in my game, there is an option to use items on one another. For each pair of items, the game performs an action.
Currently, there are two variables that game uses for this: "ItemUsed" and "ItemUsedOn". First, the game chooses first item - its id goes to "ItemUsed", then he chooses second item, it's id goes to "ItemUsedOn". Then, there is a void that defines a specific action.
A short example of code:
if (ItemUsed == "itm_cable")
{
if (ItemUsedOn == "itm_towel")
SubMain.ItemsMergedText = "To achieve what?";
else if (ItemUsedOn == "itm_wet_towel")
SubMain.ItemsMergedText = "No, water will damage it";
else if (ItemUsedOn == "itm_glass")
SubMain.ItemsMergedText = "I don't need to cut the cable";
}
if (ItemUsed == "itm_book_electronics")
{
if (ItemUsedOn == "itm_towel")
SubMain.ItemsMergedText = "Why?";
if (ItemUsedOn == "itm_wet_towel")
SubMain.ItemsMergedText = "No, water will damage the book";
else if (ItemUsedOn == "itm_soap")
SubMain.ItemsMergedText = "Wrong plan";
else if (ItemUsedOn == "itm_hair")
SubMain.ItemsMergedText = "It won't help";
}
And there are many such pairs.
However, there is a problem with this approach. When two items are combined, their order doesn't matter, for example "itm_toolbox" can be "ItemUsed" and "itm_cable" can be "ItemUsedOn", but also can be the other way around, the result will be the same. How can this be achieved?
I did try using this in every "larger" if:
else
CombineItems(ItemUsedOn, "itm_book_edda");
But this doesn't always work, and I couldn't find why.
So, what I am looking for is function that gets 2 variables:
void CombineItems(string Item1, string Item2)
And then give same result in those cases:
if (Item1="Tomato")&&(Item2="Cucumber")
Item3="Salad"
if (Item1="Cucumber")&&(Item2="Tomato")
Item3="Salad"
My question: is there an easier way for this, without using so many "if's"?
Thank you in advance,
Evgenie
I'd recommend that you create yourself an eg UnorderedPair type, which overrides the behaviour of .Equals and/or ==, such that:
new UnorderedPair("Tomato", "Cumcumber") == new UnorderedPair("Cucumber", "Tomato");
Then you can reduce all of your if statements down to a simple dictionary:
combinedItems = new Dictionary<UnderedPair, string>
{
[new UnorderedPair("Tomato", "Cumcumber")] = "Salad",
[new UnorderedPair("Bread", "Filling")] = "Sandwich",
...
};
and your code for determining the text can then just be:
SubMain.ItemsMergedText = combinedItems[new UnorderedPair(ItemUsed, ItemUsedOn)];
You can use params(to treat them as array) and LINQ. Store the salad-items in an array too:
private string[] SaladItems = new string[] { "Tomato", "Cucumber" };
string CombineItems(params string[] Items)
{
bool isSalad = SaladItems.Length == Items.Length && !SaladItems.Except(Items).Any();
if (isSalad) return "Salad";
// other types ...
return null; // no match, exception?
}
Side-Note: if you want to accept "tomato"(so ignore the case) use:
!SaladItems.Except(Items, StringComparer.InvariantCultureIgnoreCase).Any()
You could make this endless list of comparisons a lot smaller if you structure the logic into separate parts.
First, I would create a list or so to keep the items and their result:
var items = new[] { new { Item1 = "tomato", Item2 = "Cucumber", Result = "Salad" }
, new { Item1 = "tomato2", Item2 = "Cucumber2", Result = "Salad2" }
};
Then find the matching item:
var match = items.FirstOrDefault
(itm => (itm.Item1 == Item1 && itm.Item2 == Item2)
|| (itm.Item1 == Item2 && itm.Item2 == Item1)
);
There are two approaches that can keep down the amount of duplication throughout the code:
Sort the items so they're alphabetical order:
if(Item1 > Item2) {
var tmp = Item2;
Item2 = Item1;
Item1 = tmp;
}
now all of your remaining code can assume that Item1 values will always be earlier alphabetically than Item2, so you'd only write:
if (Item1="Cucumber")&&(Item2="Tomato")
Item3="Salad"
Or, you can, after exhausting all of your options (again, written only once), call your method recursively with the parameters swapped:
void TakeAction(Item1, Item2) {
.
.
.
/* No matches */
else {
TakeAction(Item2,Item1);
}
}
var items= new HashSet<string>();
items.Add("tomato");
items.Add("Cucumber");
if(items.Contains("tomato") && items.Contains("Cucumber")) //Order does **NOT** matter
Item3="Salad";

Find out if string list items startswith another item from another list

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

Compare List<string> and enable the required control

I am having two list<string> as follows
listA is having the following values 10,20,30,40 and
listB is having the following values 10,20,30
If listB contains listA elements i would like to enable particular controls if not i would like to disable the Controls
I tried of using two loops as follows
for(int ilist1=0;ilist1<listA.count;ilist1++)
{
for(int ilist2=0;ilist2<listB.count;ilist2++)
{
if(listA[ilist1]==listB[ilist2])
{
//Enable particular control
}
}
}
But i know this is not an appropriate one to do so can any one tell me the best approach to achieve this
What you want to do is to hash the items in the first list into a set then verify for each item in the second is within the set. Unfortunately the HashSet<> is not available so the closest you can get is the Dictionary<,>.
Do this:
Dictionary<string, string> set = new Dictionary<string, string>();
foreach (string item in listA)
{
set.Add(item, item);
}
foreach (string item in listB)
{
if (!set.ContainsKey(item))
{
//Enable particular control
}
}
It's easy by using the Intersect method:
if (listB.Intersect(listA).Count() > 0)
{
//enable Control
}
else
{
//disable control
}
I think you are looking for something like this
List<string> lista = new List<string>() {"10","40","30" };
List<string> listb = new List<string>() { "10", "20" };
var diff = listb.Except<string>(lista);
diff should give you the ones which didn't match else all would have been matched.
For 2.0
if (listb.TrueForAll(delegate(string s2) { return lista.Contains(s2); }))
MessageBox.Show("All Matched");
else
MessageBox.Show("Not Matched");
In fx 2.0, you can do it like this:
string b = listA.Find(delegate(string a) { return listB.Contains(a); });
if (string.IsNullOrEmpty(b))
{
//disable control
}
else
{
//enable control
}
Control.Enabled = listB.Intersect(listA).Any()
Note that Any() will only check to see if there is at least one item. Count() > 0 will evaluate the entire collection when you only need to check if there is at least one item
Edit: If you are in a .NET 2.0 environment then you can loop through and do this:
foreach (int item in listB)
{
if (listA.Contains(item))
return true;
}
return false;

C# dedupe List based on split

I'm having a hard time deduping a list based on a specific delimiter.
For example I have 4 strings like below:
apple|pear|fruit|basket
orange|mango|fruit|turtle
purple|red|black|green
hero|thor|ironman|hulk
In this example I should want my list to only have unique values in column 3, so it would result in an List that looks like this,
apple|pear|fruit|basket
purple|red|black|green
hero|thor|ironman|hulk
In the above example I would have gotten rid of line 2 because line 1 had the same result in column 3. Any help would be awesome, deduping is tough in C#.
how i'm testing this:
static void Main(string[] args)
{
BeginListSet = new List<string>();
startHashSet();
}
public static List<string> BeginListSet { get; set; }
public static void startHashSet()
{
string[] BeginFileLine = File.ReadAllLines(#"C:\testit.txt");
foreach (string begLine in BeginFileLine)
{
BeginListSet.Add(begLine);
}
}
public static IEnumerable<string> Dedupe(IEnumerable<string> list, char seperator, int keyIndex)
{
var hashset = new HashSet<string>();
foreach (string item in list)
{
var array = item.Split(seperator);
if (hashset.Add(array[keyIndex]))
yield return item;
}
}
Something like this should work for you
static IEnumerable<string> Dedupe(this IEnumerable<string> input, char seperator, int keyIndex)
{
var hashset = new HashSet<string>();
foreach (string item in input)
{
var array = item.Split(seperator);
if (hashset.Add(array[keyIndex]))
yield return item;
}
}
...
var list = new string[]
{
"apple|pear|fruit|basket",
"orange|mango|fruit|turtle",
"purple|red|black|green",
"hero|thor|ironman|hulk"
};
foreach (string item in list.Dedupe('|', 2))
Console.WriteLine(item);
Edit: In the linked question Distinct() with Lambda, Jon Skeet presents the idea in a much better fashion, in the form of a DistinctBy custom method. While similar, his is far more reusable than the idea presented here.
Using his method, you could write
var deduped = list.DistinctBy(item => item.Split('|')[2]);
And you could later reuse the same method to "dedupe" another list of objects of a different type by a key of possibly yet another type.
Try this:
var list = new string[]
{
"apple|pear|fruit|basket",
"orange|mango|fruit|turtle",
"purple|red|black|green",
"hero|thor|ironman|hulk "
};
var dedup = new List<string>();
var filtered = new List<string>();
foreach (var s in list)
{
var filter = s.Split('|')[2];
if (dedup.Contains(filter)) continue;
filtered.Add(s);
dedup.Add(filter);
}
// Console.WriteLine(filtered);
Can you use a HashSet instead? That will eliminate dupes automatically for you as they are added.
May be you can sort the words with delimited | on alphabetical order. Then store them onto grid (columns). Then when you try to insert, just check if there is column having a word which starting with this char.
If LINQ is an option, you can do something like this:
// assume strings is a collection of strings
List<string> list = strings.Select(a => a.Split('|')) // split each line by '|'
.GroupBy(a => a[2]) // group by third column
.Select(a => a.First()) // select first line from each group
.Select(a => string.Join("|", a))
.ToList(); // convert to list of strings
Edit (per Jeff Mercado's comment), this can be simplified further:
List<string> list =
strings.GroupBy(a => a.split('|')[2]) // group by third column
.Select(a => a.First()) // select first line from each group
.ToList(); // convert to list of strings

Collection was modified; enumeration operation may not execute in ArrayList [duplicate]

This question already has answers here:
How to remove elements from a generic list while iterating over it?
(28 answers)
Closed 9 years ago.
I'm trying to remove an item from an ArrayList and I get this Exception:
Collection was modified; enumeration operation may not execute.
Any ideas?
You are removing the item during a foreach, yes? Simply, you can't. There are a few common options here:
use List<T> and RemoveAll with a predicate
iterate backwards by index, removing matching items
for(int i = list.Count - 1; i >= 0; i--) {
if({some test}) list.RemoveAt(i);
}
use foreach, and put matching items into a second list; now enumerate the second list and remove those items from the first (if you see what I mean)
Here's an example (sorry for any typos)
var itemsToRemove = new ArrayList(); // should use generic List if you can
foreach (var item in originalArrayList) {
if (...) {
itemsToRemove.Add(item);
}
}
foreach (var item in itemsToRemove) {
originalArrayList.Remove(item);
}
OR if you're using 3.5, Linq makes the first bit easier:
itemsToRemove = originalArrayList
.Where(item => ...)
.ToArray();
foreach (var item in itemsToRemove) {
originalArrayList.Remove(item);
}
Replace "..." with your condition that determines if item should be removed.
One way is to add the item(s) to be deleted to a new list. Then go through and delete those items.
I like to iterate backward using a for loop, but this can get tedious compared to foreach. One solution I like is to create an enumerator that traverses the list backward. You can implement this as an extension method on ArrayList or List<T>. The implementation for ArrayList is below.
public static IEnumerable GetRemoveSafeEnumerator(this ArrayList list)
{
for (int i = list.Count - 1; i >= 0; i--)
{
// Reset the value of i if it is invalid.
// This occurs when more than one item
// is removed from the list during the enumeration.
if (i >= list.Count)
{
if (list.Count == 0)
yield break;
i = list.Count - 1;
}
yield return list[i];
}
}
The implementation for List<T> is similar.
public static IEnumerable<T> GetRemoveSafeEnumerator<T>(this List<T> list)
{
for (int i = list.Count - 1; i >= 0; i--)
{
// Reset the value of i if it is invalid.
// This occurs when more than one item
// is removed from the list during the enumeration.
if (i >= list.Count)
{
if (list.Count == 0)
yield break;
i = list.Count - 1;
}
yield return list[i];
}
}
The example below uses the enumerator to remove all even integers from an ArrayList.
ArrayList list = new ArrayList() {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
foreach (int item in list.GetRemoveSafeEnumerator())
{
if (item % 2 == 0)
list.Remove(item);
}
Don't modify the list inside of a loop which iterates through the list.
Instead, use a for() or while() with an index, going backwards through the list. (This will let you delete things without getting an invalid index.)
var foo = new List<Bar>();
for(int i = foo.Count-1; i >= 0; --i)
{
var item = foo[i];
// do something with item
}
Am I missing something? Somebody correct me if I'm wrong.
list.RemoveAll(s => s.Name == "Fred");
Instead of foreach(), use a for() loop with a numeric index.
I agree with several of the points I've read in this post and I've incorporated them into my solution to solve the exact same issue as the original posting.
That said, the comments I appreciated are:
"unless you are using .NET 1.0 or 1.1, use List<T> instead of ArrayList. "
"Also, add the item(s) to be deleted to a new list. Then go through and delete those items."
.. in my case I just created a new List and the populated it with the valid data values.
e.g.
private List<string> managedLocationIDList = new List<string>();
string managedLocationIDs = ";1321;1235;;" // user input, should be semicolon seperated list of values
managedLocationIDList.AddRange(managedLocationIDs.Split(new char[] { ';' }));
List<string> checkLocationIDs = new List<string>();
// Remove any duplicate ID's and cleanup the string holding the list if ID's
Functions helper = new Functions();
checkLocationIDs = helper.ParseList(managedLocationIDList);
...
public List<string> ParseList(List<string> checkList)
{
List<string> verifiedList = new List<string>();
foreach (string listItem in checkList)
if (!verifiedList.Contains(listItem.Trim()) && listItem != string.Empty)
verifiedList.Add(listItem.Trim());
verifiedList.Sort();
return verifiedList;
}
using ArrayList also you can try like this
ArrayList arraylist = ... // myobject data list
ArrayList temp = (ArrayList)arraylist.Clone();
foreach (var item in temp)
{
if (...)
arraylist.Remove(item);
}

Categories

Resources