How to iterate for larger list using foreach - c#

I have two list ,list1 of size 5 elements and list2 of size 6 elements.
I want to iterate for larger list size(eg. 6 in this case) using foreach statement but the problem is I am not using if condition to check which list is larger .
So how can I do the necessary task.
if (list1.Count>list2.Count) // here I donot want to use if statement
{ // do it in 1 statement only
Size=list1.Count;
foreach (var item in list1)
{
// do something
}
}
else
{
Size = list2.Count;
foreach (var item in list2)
{
// do something
}
}

You can move the condition into the foreach:
foreach(var item in (list1.Count > list2.Count ? list1 : list2))
{
// do stuff
}
If you have several Lists (more than 2), you can create collection and get the maximum using LINQ:
var myLists = new List<List<T>>(); // T is the generic type of your Lists, obviously
myLists.Add(list1);
myLists.Add(list2);
...
myLists.Add(listN);
// iterate over the largest one:
foreach (var item in myLists.First(l => l.Count == lists.Max(x=>x.Count)))
{
// do stuff
}

var list = list1.Count > list2.Count ? list1 : list2;
foreach(var item in list)

Related

Loop to check for duplicate strings

I want to create a loop to check a list of titles for duplicates.
I currently have this:
var productTitles = SeleniumContext.Driver.FindElements(By.XPath(ComparisonTableElements.ProductTitle));
foreach (var x in productTitles)
{
var title = x.Text;
productTitles = SeleniumContext.Driver.FindElements(By.XPath(ComparisonTableElements.ProductTitle));
foreach (var y in productTitles.Skip(productTitles.IndexOf(x) + 1))
{
if (title == y.Text)
{
Assert.Fail("Found duplicate product in the table");
}
}
}
But this is taken the item I skip out of the array for the next loop so item 2 never checks it's the same as item 1, it moves straight to item 3.
I was under the impression that skip just passed over the index you pass in rather than removing it from the list.
You can use GroupBy:
var anyDuplicates = SeleniumContext
.Driver
.FindElements(By.XPath(ComparisonTableElements.ProductTitle))
.GroupBy(p => p.Text, p => p)
.Any(g => g.Count() > 1);
Assert.That(anyDuplicates, Is.False);
or Distinct:
var productTitles = SeleniumContext
.Driver
.FindElements(By.XPath(ComparisonTableElements.ProductTitle))
.Select(p => p.Text)
.ToArray();
var distinctProductTitles = productTitles.Distinct().ToArray();
Assert.AreEqual(productTitles.Length, distinctProductTitles.Length);
Or, if it is enough to find a first duplicate without counting all of them it's better to use a HashSet<T>:
var titles = new HashSet<string>();
foreach (var title in SeleniumContext
.Driver
.FindElements(By.XPath(ComparisonTableElements.ProductTitle))
.Select(p => p.Text))
{
if (!titles.Add(title))
{
Assert.Fail("Found duplicate product in the table");
}
}
All approaches are better in terms of computational complexity (O(n)) than what you propose (O(n2)).
You don't need a loop. Simply use the Where() function to find all same titles, and if there is more than one, then they're duplicates:
var productTitles = SeleniumContext.Driver.FindElements(By.XPath(ComparisonTableElements.ProductTitle));
foreach(var x in productTitles) {
if (productTitles.Where(y => x.Text == y.Text).Count() > 1) {
Assert.Fail("Found duplicate product in the table");
}
}
I would try a slightly different way since you only need to check for duplicates in a one-dimensional array.
You only have to check the previous element with the next element within the array/collection so using Linq to iterate through all of the items seems a bit unnecessary.
Here's a piece of code to better understand:
var productTitles = SeleniumContext.Driver.FindElements(By.XPath(ComparisonTableElements.ProductTitle))
for ( int i = 0; i < productionTitles.Length; i++ )
{
var currentObject = productionTitles[i];
for ( int j = i + 1; j < productionTitles.Length; j++ )
{
if ( currentObject.Title == productionTitles[j].Title )
{
// here's your duplicate
}
}
}
Since you've checked that item at index 0 is not the same as item placed at index 3 there's no need to check that again when you're at index 3. The items will remain the same.
The Skip(IEnumerable, n) method returns an IEnumerable that doesn't "contain" the n first element of the IEnumerable it's called on.
Also I don't know what sort of behaviour could arise from this, but I wouldn't assign a new IEnumerable to the variable over which the foreach is being executed.
Here's another possible solution with LINQ:
int i = 0;
foreach (var x in productTitles)
{
var possibleDuplicate = productTitles.Skip(i++).Find((y) => y.title == x.title);
//if possibleDuplicate is not default value of type
//do stuff here
}
This goes without saying, but the best solution for you will depend on what you are trying to do. Also, I think the Skip method call is more trouble than it's worth, as I'm pretty sure it will most certainly make the search less eficient.

C# Delete one of two successive and same lines in a list

how can i delete one of two same successive lines in a list?
For example:
load
testtest
cd /abc
cd /abc
testtest
exit
cd /abc
In this case ONLY line three OR four.The lists have about 50000 lines, so it is also about speed.
Do you have an idea?
Thank you!
Homeros
You just have to look at the last added element in the second list:
var secondList = new List<string>(firstList.Count){ firstList[0] };
foreach(string next in firstList.Skip(1))
if(secondList.Last() != next)
secondList.Add(next);
Since you wanted to delete the duplicates you have to assign this new list to the old variable:
firstList = secondList;
This approach is more efficient than deleting from a list.
Side-note: since Enumerable.Last is optimized for collections with an indexer(IList<T>), is is as efficient as secondList[secondList.Count-1], but more readable.
user a reverse for-loop and check the adjacent elements:
List<string> list = new List<string>();
for (int i = list.Count-1; i > 0 ; i--)
{
if (list[i] == list[i-1])
{
list.RemoveAt(i);
}
}
the reverse version is advantageous here, because the list might shrink in size with every removed element
I would first split the list, then use LINQ to only select items that don't have the same previous item:
string[] source = text.Split(Environment.NewLine);
var list = source.Select((l, idx) => new { Line = l, Index = idx } )
.Where(x => x.Index == 0 || source[x.Index - 1] != x.Line)
.Select(x => x.Line)
.ToList() // materialize
;
O(n) as extension method
public static IEnumerable<string> RemoveSameSuccessiveItems(this IEnumerable<string> items)
{
string previousItem = null;
foreach(var item in list)
{
if (item.Equals(previousItem) == false)
{
previousItem = item;
yield item;
}
}
}
Then use it
lines = lines.RemoveSameSuccessiveItems();

Replace foreach with lambda while updating list

I want to update vmlist by geting values from vlist without using any foreach loop.
For now I am just doing this with foreach loop, but I want to replace this foreach with LINQ
foreach (var item in vlist){
vmlist.Where(list => list.SId==item.SId && list.ParameterId==item.ParameterId && list.WId==item.WId)
.Select(li => { li.Value = item.Value; return li; }).ToList();
}
Your current approach is very inefficient - it's O(N * M) and it creates a list on each iteration.
Using a join would be more efficient - I would still use a foreach loop, but separate the querying part from the update part:
var pairsToUpdate = from original in vmlist
join item in vlist
on new { original.SId, original.ParameterId, original.WId }
equals new { item.SId, item.ParameterId, item.WId }
select new { original, item };
foreach (var pair in pairsToUpdate)
{
pair.original.Value = pair.item.Value;
}
No abuse of Select with side-effects
No extra lists created for no good reason
More efficient selection of items to update

C# count consecutive duplicates in List<string>

I have a List of strings and want to count the duplicates in it, to work with this information later. To simply count the duplicates would be very easy, but unfortunately I just want to count the consecutive duplicates.
Let us say we have a list with this string items in it:
"1A","3B","5X","7Q","2W","2G","2J","1A","2A"
Now I want to count the duplicates in this list. I just will look at the first char of each string, the other characters in the string can be ignored!
What we get is 2x "1%" and 3x "2%", what i actually want to get is consecutive duplicates, so my result should look like 3x "2%". The 2x "1A" has to be ignored, they are not in a row.
(% = place holder)
I wrote a code that loops through the list and compares one string with the next one
int counter = 0;
for (int i = 0; i < list.Count; i++)
{
char first = list[i][0];
if ((i + 1) == list.Count) break;
char second = list[(i + 1)][0];
if (first == second)
{
counter++;
}
}
I guess you can imagine that this code is a very ugly way to do this especially if you want to work with the output. Also my code can´t handle the features I need.
The code I am looking for, must be able to deal with two feature I want to implement. First, a row of duplicates does not end if the last element of my list is equal to the first element of the list.
For example:
"1A","1B","5X","7Q","2J","1I"
The "1%" has to be detected as duplicate, because of the "1I" and "1A" which are "in a row". If you would loop through the list you just break up at the end of the list if the first and last element are not equal.
pseudo code:
if(list.First()[0] != list.Last()[0])
The second feature I want to implement is, that the items in the list that are not duplicates, with a "duplicate count" over 4 will be deleted. If there is not a single duplicate row with a "duplicate count" or length over 4 I want to return.
For example:
"1A","1B","5X","3Q","1J","1I"
duplicate count == 4 so return
"1A","1B","1X","3Q","1J","1I"
duplicate count == 5, save this five items, delete any other item in the list.
"1A","1B","1X","3Q","1I","1Z","1Z"
duplicate count == 6, save this six items, delete any other item in the list.
Notice:
Just the first char of each string matters. The input list will have 7 items, not a single item more or less. There is no result list, the old one has to be updated. If the duplicate count is under or equal to 4, then there is no work to do, simply return.
There will not be more than 5 duplicates in a row. I have to check billion of list, so performance is really important
As they don´t teach any better English in German schools, I hope anyone understand what my problem is and is willing to help me out.
This is not part of any homework.
What you can use here is a method that is capable of grouping consecutive items while a condition is met:
public static IEnumerable<IEnumerable<T>> GroupWhile<T>(
this IEnumerable<T> source, Func<T, T, bool> predicate)
{
using (var iterator = source.GetEnumerator())
{
if (!iterator.MoveNext())
yield break;
List<T> list = new List<T>() { iterator.Current };
T previous = iterator.Current;
while (iterator.MoveNext())
{
if (!predicate(previous, iterator.Current))
{
yield return list;
list = new List<T>();
}
list.Add(iterator.Current);
previous = iterator.Current;
}
yield return list;
}
}
Once we have this helper method we can write your query in a reasonably straightforward manor:
var query = data.GroupWhile((prev, current) => prev[0] == current[0])
.Where(group => group.Count() > 1)
.Select(group => new
{
Character = group.First()[0],
Count = group.Count(),
});
I suggest you to group items beginning with the same char in lists. The result of this grouping will be a List<List<string>>. This makes it easier to work with groups.
var list = new List<string> {
"1A", "3B", "5X", "7Q", "2W", "2G", "2J", "1B", "1C", "1D", "1E"
};
var groups = new List<List<string>>();
char lastChar = (char)0; // We assume that NUL will never be used as first char.
List<string> group = null;
foreach (string s in list) {
if (s[0] != lastChar) {
group = new List<string>();
groups.Add(group);
lastChar = s[0];
}
group.Add(s);
}
// Join the first and the last group if their first char is equal
int lastIndex = groups.Count - 1;
if (groups.Count > 2 && groups[0][0][0] == groups[lastIndex][0][0]) {
// Insert the elements of the last group to the first group
groups[0].InsertRange(0, groups[lastIndex]);
// and delete the last group
groups.RemoveAt(lastIndex);
}
//TODO: Remove test
foreach (List<string> g in groups) {
Console.WriteLine(g[0][0]);
foreach (string s in g) {
Console.WriteLine(" " + s);
}
}
// Now create a list with items of groups having more than 4 duplicates
var result = new List<string>();
foreach (List<string> g in groups) {
if (g.Count > 4) {
result.AddRange(g);
}
}
//TODO: Remove test
Console.WriteLine("--------");
foreach (string s in result) {
Console.Write(s);
Console.Write(" ");
}
Console.WriteLine();
Console.ReadKey();

How to Map Id in List?

Dictionary<int, string> lstSrc = new Dictionary<int, string>();
Dictionary<int, string> lstDest = new Dictionary<int, string>();
lstSrc.Add(1, "All");
lstSrc.Add(2, "Weekday");
lstSrc.Add(3, "WeekEnd");
lstDest.Add(1, "All");
lstDest.Add(2, "X1");
lstDest.Add(3, "X2");
lstDest.Add(4, "Weekday");
lstDest.Add(5, "WeekEnd");
Compare only when name matches in Source and Destination
var matchingItems = lstDest
.Where(l2 => lstSrc.Any(l1 => l1.Value.Equals(l2.Value))).ToList();
matchingItems.AddRange(lstDest.Except(matchingItems));
This query gives result as see in attached image how to get that result without using LINQ ?
How i can achieve this ?
[1]: http://i.stack.imgur.com/FLicZ.png
To get the matching items you could use a query like this:
var matchingItems = List2
.Where(l2 => List1.Any(l1 => l1.TimeGroupName.Equals(l2.TimeGroupName));
matchingItems.AddRange(List2.Except(matchingItems)));
Edited: equivalent without using Linq: (It's easy to forget how much boiler plate Linq saves you from writing!)
// Get the matching items
List<TIMEGROUPINFO> matchingItems = new List<TIMEGROUPINFO>();
foreach (TIMEGROUPINFO l1 in List1)
{
foreach (TIMEGROUPINFO l2 in List2)
{
if (l1.TimeGroupName.Equals(l2.TimeGroupName))
{
matchingItems.Add(l1);
continue;
}
}
}
// Append the items from List2 which aren't already in the list:
foreach (TIMEGROUPINFO l2 in List2)
{
bool exists = false;
foreach (TIMEGROUPINFO match in matchingItems)
{
if (match.TimeGroupName.Equals(l2.TimeGroupName))
{
// This item is already in the list.
exists = true;
break;
}
}
if (exists = false)
matchingItems.Add(l2);
}
I understand that you want to perform a query on list 2 based on list 1. Linq is very good for that.
so, if you wrote something like
//List1Element is a single element in the first list.
List1Element = List1[i];
List2.Where(l2 => l2.TimeGroupName == List1Element.TimeGroupName).ToList();
That might accomplish what I think you're trying to accomplish.
If you're trying to match the entire List1 at once, you can either iterate through all the list1 elements, or you can look into Linq Join operations

Categories