Checking the equality of lists in foreach loop - C# - c#

I'm trying to check the equality of 2 lists of string but it's not working. This is what I have done:
foreach (List<string> q in questions)
{
if (!groupOfQuestions.Except(q).Any()) //I also tried without '!'
{
questions.Add(groupOfQuestions);
}
}
And declaration of lists:
List<List<string>> questions = new List<List<string>>();
List<string> groupOfQuestions = new List<string>();

You can't modify collection within foreach loop, but you can do it in for loop:
for (int i = questions.Count - 1; i >= 0; --i) {
List<string> q = questions[i];
if (!groupOfQuestions.Except(q).Any()) //I also tried without '!'
questions.Add(groupOfQuestions);
}
Another possibility is to loop on collection's copy:
// Note ".ToList()"
foreach (List<string> q in questions.ToList())
if (!groupOfQuestions.Except(q).Any()) //I also tried without '!'
questions.Add(groupOfQuestions);

Your problem is on this line:
questions.Add(groupOfQuestions);
You cannot modify a collection while you are iterating over it.
You're going to need to create a new List<List<string>> that you can add the matches to. For example:
var questions = new List<List<string>> {
new List<string>{"aaa", "bbb", "ccc"},
new List<string>{"aaa", "bbb", "ddd"},
};
var groupOfQuestions = new List<string>() { "ddd" };
var questionMatches = new List<List<string>>();
foreach (List<string> q in questions)
{
if (!groupOfQuestions.Except(q).Any()) //I also tried without '!'
{
questionMatches.Add(groupOfQuestions);
}
}

questions can not be modified when iterating over it in a foreach. Build a new list and run AddRange at the end:
var listsToAdd = new List<List<string>>();
foreach (List<string> q in questions)
{
if (!groupOfQuestions.Except(q).Any())
{
questions.Add(groupOfQuestions);
}
}
questions.AddRange(listsToAdd);

Related

Removing strings from a list line by line

My question is, if I have a list that looks something like the following,
var list = new List<string>();
list.Add("12345");
list.Add("Words");
list.Add("Are");
list.Add("Here");
list.Add("13264");
list.Add("More");
list.Add("Words");
list.Add("15654");
list.Add("Extra");
list.Add("Words");
And I want to be able to delete all the strings that start with numbers from the list and also concatenate the strings in between them so that it looks like the following,
Words Are Here
More Words
Extra Words
How does that logic look? Below is what I have been trying to do, but I can't first out how to delete the strings with number much less create a newline when i delete a string with numbers.
foreach (string s in list)
{
if (s.StartsWith("1"))
s.Remove(0, s.Length);
else
String.Concat(s);
}
foreach (string p in list)
Console.WriteLine(p);
What you're doing is "chunking" or "paging" your data, but you need to walk through each source element one by one to determine where your pages start and stop.
public static IEnumerable<ICollection<T>> ChunkBy<T>(IEnumerable<T> source, Func<T, bool> predicate)
{
ICollection<T> currentChunk = new List<T>();
foreach (var item in source)
{
if (predicate(item))
{
if (currentChunk.Any())
{
yield return currentChunk;
currentChunk = new List<T>();
}
}
else
{
currentChunk.Add(item);
}
}
if (currentChunk.Any())
{
yield return currentChunk;
}
}
This is a reusable method (and you could add this to the start of the first parameter to make it an extension method) that takes advantage of IEnumerable and yield-ing results. In essence, you return a stream of values, where each value is a collection. So it's a list of a list, but with much more fluid terms.
What this does is
Start with an empty temporary list for the current "page" of items.
Loop through each element in your source.
For each element, decide if it's the start of a new page or not.
If it is a new block, return what you've saved up for the current chuck, assuming that saved chunk is not empty.
If it is not a new block, add the entry to the current chunk.
When you're done going through all the entries, send the final chunk, assuming it's not empty.
You can call this method like this:
var output = ChunkBy(list, x => char.IsNumber(x[0]))
.Select(ch => string.Join(" ", ch));
foreach (var o in output)
Console.WriteLine(o);
Which gives
Words Are Here
More Words
Extra Words
You could try something like:
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var list = new List<string>();
list.Add("12345");
list.Add("Words");
list.Add("Are");
list.Add("Here");
list.Add("13264");
list.Add("More");
list.Add("Words");
list.Add("15654");
list.Add("Extra");
list.Add("Words");
var resultStrings = new List<string>();
string currentString = "";
foreach (string s in list)
{
if (s.StartsWith("1"))
{
resultStrings.Add(currentString);
currentString = "";
}
else
{
currentString += s + " ";
}
}
resultStrings.Add(currentString);
foreach (string p in resultStrings)
Console.WriteLine(p);
}
}
basically I am looping thou the list if the value is not starting with 1 I add the value to the currentString.
When the value does start with 1 we add the currentString to the resultStrings an start a new currentString.
There are some improvements possible, the if with starts withs 1 is not completely fool prove.
You could use int.Parse.
Is multiple items in a row start with 1 you can end up with empty string in the resultStrings
You could fix this bij checking is the string is empty before adding it to the list
Another approach
var list = new List<string>();
list.Add("12345");
list.Add("Words");
list.Add("Are");
list.Add("Here");
list.Add("13264");
list.Add("More");
list.Add("Words");
list.Add("15654");
list.Add("Extra");
list.Add("Words");
var lines = new List<KeyValuePair<int, string>>();
var currentIndex = 0;
foreach (var line in list.Where(x => x.Length > 0))
{
var firstChar = line.Substring(0, 1)
.ToCharArray()
.First();
if (char.IsNumber(firstChar))
{
currentIndex++;
continue;
}
lines.Add(new KeyValuePair<int, string>(currentIndex, line));
}
foreach (var lineGroup in lines.GroupBy(x => x.Key))
{
Console.WriteLine(string.Join(" ", lineGroup.Select(x => x.Value)));
}
I grouped your words and then printed them like this.
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
var list = new List<string>();
// list.Add("Some"); <-- still works
// list.Add("Words");
list.Add("12345");
list.Add("Words");
list.Add("Are");
list.Add("Here");
list.Add("13264");
list.Add("More");
list.Add("Words");
list.Add("15654");
list.Add("Extra");
list.Add("Words");
// list.Add("1234566"); <-- still works
var groups = new Dictionary<int, List<string>>();
int gnum = 0;
foreach (string item in list)
{
if (long.TryParse(item, out long val))
groups.Add(++gnum, new List<string>());
else
{
if (!groups.ContainsKey(gnum))
groups.Add(gnum, new List<string>());
groups[gnum].Add(item);
}
}
foreach (var kvp in groups)
{
string result = string.Join(" ", kvp.Value);
Console.WriteLine(result);
}
}
}
Here is a simple linq that should do what you want.
String.Join(" ", list.Where(m => !char.IsDigit(m[0])))

How do I merge two lists in a single list without taking union or join in C # without duplicates?

italic
Problem :
We have the Student class with a single field Name (string).Build a function that takes as input two student lists and returns a single list that contains elements of two input lists, excluding those with duplicate names. italic
Here is my code:
using System;
using System.Collections.Generic;
namespace ExerciseGeneric(listA, listB)
{
class Program
{
static void Main(string[] args)
{
List<string> ListA = new List<string>
{
"James",
"Jon",
"Mark",
"Jey",
"Sara",
};
List<string> ListB = new List<string>
{
"Peter",
"Parker",
"Bond",
"Sara"
};
List<string> ListResult = new List<string>();
foreach (var item in ListA)
{
ListResult.Add(item);
}
foreach (var item in ListB)
{
ListResult.Add(item);
}
for (var i = 0; i < ListResult.Count; ++i)
{
for (var j = i + 1; j < ListResult.Count; ++j)
{
if (ListResult[i] == ListResult[j])
{
ListResult.Remove(j, 1);
}
break;
}
foreach (var result in ListResult)
{
Console.WriteLine("ListResult : " + result);
}
}
}
}
}
If you want to remove duplicates I suggest using set, e.g. HashSet<string>:
HashSet<string> combined = new HashSet<string>(ListA);
combined.UnionWith(ListB);
List<string> ListResult = new List<string>(combined.Count);
foreach (string item in combined)
ListResult.Add(item);
Or if using UnionWith is cheating:
HashSet<string> combined = new HashSet<string>();
List<string> ListResult = new List<string>();
foreach (string item in ListA)
if (combined.Add(item)) // if item is unique (i.e. added to the set)
ListResult.Add(item); // we add it into the list as well
foreach (string item in ListB)
if (combined.Add(item))
ListResult.Add(item);
Finally, if everything but ListResult is prohibited (disclamer: List<T> is not very good choice for Contains):
List<string> ListResult = new List<string>();
foreach (string item in ListA)
if (!ListResult.Contains(item))
ListResult.Add(item);
foreach (string item in ListB)
if (!ListResult.Contains(item))
ListResult.Add(item);
you can easily turn foreach loop into for one:
for (int i = 0; i < ListA.Count; ++i) {
string item = listA[i];
if (!ListResult.Contains(item))
ListResult.Add(item);
}
for (int i = 0; i < ListB.Count; ++i) {
string item = listB[i];
if (!ListResult.Contains(item))
ListResult.Add(item);
}
You can use the AddRange method to combine the two lists then use Distinct to get rid of the duplicates.
List<string> namesA = new List<string>() { "A", "B", "C" };
List<string> namesB = new List<string>() { "D", "A", "B" };
namesA.AddRange(namesB);
List<string> combined = namesA.Distinct().ToList();
combined.ForEach(x => Console.WriteLine(x));
prints:
A
B
C
D
You can also do the following.. this keeps the original lists unmodified.
List<string> namesA = new List<string>() { "A", "B", "C" };
List<string> namesB = new List<string>() { "D", "A", "B" };
List<string> combined = new List<string>();
combined.AddRange(namesA);
combined.AddRange(namesB);
combined = combined.Distinct().ToList();
combined.ForEach(x => Console.WriteLine(x));
Adding to the HashSet answer by Dmitry, assuming that you are only not allowed to use Join and Union I think it another alternative is to use the .Distinct() method in System.Linq.
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
List<string> ListA = new List<string>
{
"James",
"Jon",
"Mark",
"Jey",
"Sara",
};
List<string> ListB = new List<string>
{
"Peter",
"Parker",
"Bond",
"Sara"
};
List<string> ListResult = new List<string>();
foreach (var item in ListA)
{
ListResult.Add(item);
}
foreach (var item in ListB)
{
ListResult.Add(item);
}
ListResult = ListResult.Distinct()
.ToList();
foreach(var item in ListResult)
{
Console.WriteLine(item);
}
}
}
Albeit, this answer is going to be slower compared to using HashSet.
Using Union seems to be the best option here, but if you cant:
ListA.Concat(ListB).Distinct();

c# Get only numbers from list of strings which contain numbers and words

I have a list of strings (word--number) ex (burger 5$). I need to extract only numbers from every string in list and make new int list.
There are several ways to do that, Regex and Linq for example.
For short string you can use Linq, for example:
public static void Main()
{
var myStringValue = "burger 5$";
var numbersArray = myStringValue.ToArray().Where(x => char.IsDigit(x));
foreach (var number in numbersArray)
{
Console.WriteLine(numbersArray);
}
}
If you take a look at the Regex.Split, numbers article.
You'll find the answer in there. Modified code might look like
var source = new List<string> {
"burger 5$",
"pizza 6$",
"roll 1$ and salami 2$"
};
var result = new List<int>();
foreach (var input in source)
{
var numbers = Regex.Split(input, #"\D+");
foreach (string number in numbers)
{
if (Int32.TryParse(number, out int value))
{
result.Add(value);
}
}
}
Hope it helps.
Petr
Using linq and Regex:
List<string> list = new List<string>(){"burger 5$","ab12c","12sc34","sd3d5"};
Regex nonDigits = new Regex(#"[^\d]");
List<string> list2 = list.Select(l => nonDigits.Replace(l, "")).ToList();
You can take a look and also solve the problem with this code:
List<string> word_number = new List<string>();
List<int> number = new List<int>();
word_number.Add("burger 5$");
word_number.Add("hamburger 6$");
word_number.Add("burger 12$");
foreach (var item in word_number)
{
string[] parts = item.Split(' ');
string[] string_number = parts[1].Split('$');
number.Add(Convert.ToInt16(string_number[0]));
Console.WriteLine(string_number[0]);
}

Create new list in nested list C#

Anyone know how to add new list into another list, the new list cannot be predefined.
For example, after get user input, I will do the following
List<List<string>> ListA = new List<List<string>>();
foreach (List<string> subList in ListA)
{
foreach (var value in subList)
{
if(value != INPUT)
{
// ListA needs to creates a new list with value INPUT
}
}
}
Since you cannot modify collection that you're currently enumerating use eg. ToArray():
foreach (List<string> subList in ListA.ToArray())
{
foreach (var value in subList)
{
if(value != INPUT)
(
ListA.Add(new List<string>() { INPUT });
}
}
}
You could use linq to do this:
include the namespace:
using System.Linq;
And try this:
var result = ListA.Select(x => x.Where(k => k != INPUT).ToList()).ToList();
List<List<string>> ListA = new List<List<string>>();
List<List<string>> ListsToAdd = new List<List<string>>();
foreach (List<string> subList in ListA)
{
foreach (var value in subList)
{
if(value != INPUT)
(
List<string> list = new List<string>();
list.Add(INPUT);
ListsToAdd.Add(list);
}
}
}
ListA.AddRange(ListsToAdd);

dictionary of lists in c#(cant seem to get my lists)

Here i used linq to filter my result in an array and pass into a list and from that list into a dictionary as you can see below
//array is a multidimensional array with string data in it
var datumn = array;
var list = new List<string>();
var stringcounts = new Dictionary<int,List<string>>();
var listtemp = new List<string>();
//linq
var arrayresult = from string a in datumn where a != "FREE" select a;
//adding result from arrayresult to list
foreach (var listing in arrayresult)
{
list.Add(listing);
}
//using linq again i filter my list then add to dictionary
for (int count = 3; count > 0; count-- )
{
var duplicateItems = from x in list
group x by x into grouped
where grouped.Count() == count
select grouped.Key;
foreach (var replace in duplicateItems)
{
listtemp.Add(replace.ToString());
}
stringcounts.Add(count, lists);
//clearing the list to avoid duplicating data in my dictionary
listtemp.Clear();
}
for (int key = stringcounts.Count; key > 0; --key)
{
var holding = stringcounts[key];
foreach (var li in holding)
{
MessageBox.Show(li.ToString());
//just view what i have to check if the data is correct
}
}
`
the program skips iterator over of the lists and ends can some one help with this
and i have tried everything including linq and other collections like hashtable
and maps but nothing works and it is not a console application
This line is wrong:
var dict = new Dictionary<int, List<string>>();
Remove the ";".
Result:
indigo silver violet purple green pink red brown yellow
Edit: full code for comparison:
var dict = new Dictionary<int, List<string>>()
{
{1, new List<string>{"red", "brown", "yellow"}},
{2, new List<string>{"purple", "green", "pink"}},
{3, new List<string>{"indigo", "silver", "violet"}}
};
// now i want to get my values from the lists in the dictionary
for (int count = 3; count > 0; count--)
{
var l = dict[count];
foreach (var li in l)
{
li.Dump();
}
}
foreach (var item in dict)
{
var list = item.Value;
foreach (var str in list)
{
MessageBox.Show(str);
}
}
The listtemp.Clear() is a bad syntax so therefore it should be removed and the listtemp should be declared in the for loop therefore removing redundancy and the initial problem

Categories

Resources