Access item in sequence that was skipped using LINQ's .Skip() - c#

foreach(var item in items.Where(x => x.SomeCondition == true).Skip(1))
{
item.OneThing = true;
item.AnotherThing = true;
}
For the item that was skipped using .Skip(1), I also need to set .AnotherThing to true. I can iterate everything without .Skip(1) and set .AnotherThing to true, then iterate everything with .Skip(1) and set .OneThing to true. Is there a more elegant way to do this, rather than looping through the collection twice?
Edit: What if there was a .YetAnotherThing property, which needed to be set on the item that was skipped using .Skip(1)?

Well it sounds like you don't want to use Skip in this case. Just use a local variable to remember whether this is the first iteration or not.
bool firstItem = true;
foreach(var item in items.Where(x => x.SomeCondition))
{
item.AnotherThing = true;
if (!firstItem)
{
item.OneThing = true;
}
firstItem = false;
}

Don't use Skip(1) in your foreach loop then. You can also do a Select to get the index as the second parameter.
foreach (var item in items.Where(x => x.SomeCondition)
.Select((x, i) => new { item = x, index = i })
{
// If you have a lot to do:
if (item.index != 0)
{
item.item.YetAnotherThing = 15;
item.item.OneThing = true;
}
// If you have a simple boolean
item.item.OneThing = item.index != 0;
// Something that will always happen.
item.item.AnotherThing = true;
}
Granted, in actual code, please pick better variable names than what would create item.item.

How about
var newItems = items.Where(x => x.SomeCondition == true).ToList();
if(newItems.Count != 0)
{
newItems.ForEach(i => i.AnotherThing = true);
newItems.FirstOrDefault().OneThing = true;
}

Related

Execute foreach loop fully, though if statement get executed in loop

I am using foreach loop, now what I want is this loop should run for each elements in that loop count.
Once that is finished then it should return to the function, here in this loop I have also use if statement.
But problem is when first time if statement is executing it is returning to that function without finishing whole foreach loop count.
But instead of that, it should run foreach loop fully and then return with that messages.
Below is code :
foreach (var field in fields.OrderBy(x => x.Sequence))
{
if (!header.Any(x => x.Sequence == field.Sequence && x.Name == field.Name))
{
var colName = header.Where(x => x.Sequence == field.Sequence).Select(x => x.ExcelColName).FirstOrDefault();
var newheaderName = header.Where(x => x.Sequence == field.Sequence).Select(x => x.Name).FirstOrDefault();
message = string.Format("In cell {0}1, column '{1}' was expected but '{2}' was there. The Excel data will NOT be imported.", colName, field.Name, newheaderName);
messageList.Add(message); // Here after adding all message in list, then it should return to calling function
} else
{
}
return null;
}
Put the return messageList at the end of the method.
Sidenote: you can improve performance much: don't filter on Sequence three times per iteration. Store the result of header.Where(x => x.Sequence == field.Sequence) at the beginning of the foreach in a list-variable. Then check if it contains items:
foreach (var field in fields.OrderBy(x => x.Sequence))
{
var seqHeaders = header.Where(x => x.Sequence == field.Sequence).ToList();
var matchingHeader = seqHeaders.FirstOrDefault(h => h.Name == field.Name);
if(!seqHeaders.Any())
{
// you didn't handle this case
messageList.Add($"Sequence not found '{field.Sequence}'. The Excel data will NOT be imported.");
}
else if (matchingHeader == null)
{
string colName = seqHeaders[0].ExcelColName;
string newHeaderName = seqHeaders[0].Name;
messageList.Add($"In cell {colName} column '{field.Name}' was expected but '{newHeaderName}' was there. The Excel data will NOT be imported.");
}
else
{
// use matchingHeader ...
}
}
// rest of method here ...
return messageList; // don't return null but an empty list if there were no errrors
Your return is in your foreach loop. Bring it out of the scope of the foreach.
Let's say you call your function like this.
CallMyFunction();
...
If you don't need anything to be returned from this function then use void like this.
public void CallMyFunction()
{
foreach (var field in fields.OrderBy(x => x.Sequence))
{
if (!header.Any(x => x.Sequence == field.Sequence && x.Name == field.Name))
{
var colName = header.Where(x => x.Sequence == field.Sequence).Select(x => x.ExcelColName).FirstOrDefault();
var newheaderName = header.Where(x => x.Sequence == field.Sequence).Select(x => x.Name).FirstOrDefault();
message = string.Format("In cell {0}1, column '{1}' was expected but '{2}' was there. The Excel data will NOT be imported.", colName, field.Name, newheaderName);
messageList.Add(message); // Here after adding all message in list, then it should return to calling function
} else
{
} // Remove entirely the return.
}
That way your loop will fully run.

How do I pick out values between a duplicate value in a collection?

I have a method that returns a collection that has a duplicate value.
static List<string> GenerateItems()
{
var _items = new List<string>();
_items.Add("Tase");
_items.Add("Ray");
_items.Add("Jay");
_items.Add("Bay");
_items.Add("Tase");
_items.Add("Man");
_items.Add("Ran");
_items.Add("Ban");
return _items;
}
I want to search through that collection and find the first place that duplicate value is located and start collecting all the values from the first appearance of the duplicate value to its next appearance. I want to put this in a collection but I only want the duplicate value to appear once in that collection.
This is what I have so far but.
static void Main(string[] args)
{
string key = "Tase";
var collection = GenerateItems();
int index = collection.FindIndex(a => a == key);
var matchFound = false;
var itemsBetweenKey = new List<string>();
foreach (var item in collection)
{
if (item == key)
{
matchFound = !matchFound;
}
if (matchFound)
{
itemsBetweenKey.Add(item);
}
}
foreach (var item in itemsBetweenKey)
{
Console.WriteLine(item);
}
Console.ReadLine();
}
There must be an easier way of doing this. Perhaps with Indexing or a LINQ query?
You can do something like that
string key = "Tase";
var collection = GenerateItems();
int indexStart = collection.FindIndex(a => a == key);
int indexEnd = collection.FindIndex(indexStart+1, a => a == key);
var result = collection.GetRange(indexStart, indexEnd-indexStart);
You can use linq select and group by to find the first index and last index of all duplicates (Keep in mind if something is in the list more then 2 times it would ignore the middle occurences.
But I personally think the linq for this seems overcomplicated. I would stick with simple for loops and if statements (Just turn it into a method so it reads better)
Here is a solution with Linq to get all duplicate and all values between those duplicates including itself once as you mentioned.
var collection = GenerateItems();
var Duplicates = collection.Select((x,index) => new { index, value = x })
.GroupBy(x => x.value)//group by the strings
.Where(x => x.Count() > 1)//only take duplicates
.Select(x=>new {
Value = x.Key,
FirstIndex = x.Min(y=> y.index),//take first occurenc
LastIndex = x.Max(y => y.index)//take last occurence
}).ToList();
var resultlist = new List<List<string>>();
foreach (var duplicaterange in Duplicates)
resultlist .Add(collection.GetRange(duplicaterange.FirstIndex, duplicaterange.LastIndex - duplicaterange.FirstIndex));
Try this function
public List<string> PickOut(List<string> collection, string key)
{
var index = 0;
foreach (var item in collection)
{
if (item == key)
{
return collection.Skip(index).TakeWhile(x=> x != key).ToList();
}
index++;
};
return null;
}
First finding the duplicate key then find the second occurrence of the item and then take result.
var firstduplicate = collection.GroupBy(x => x)
.Where(g => g.Count() > 1)
.Select(g => g.Key).First();
var indices = collection.Select((b, i) => b == firstduplicate ? i : -1).Where(i => i != -1).Skip(1).FirstOrDefault();
if (indices>0)
{
var result = collection.Take(indices).ToList();
}

Not able to understand LINQ extension methods

I am not able to interpret the following statement:
var myResults = new List<bool>();
myResults.Add(this.myWorkers.All(c => other.Workers.Any(cc => c.Equals(cc))));
I had a look at this article which explains Any and All separately. However I am not too sure how interpret a nested LINQ.
this.myWorkers.All(c => other.Workers.Any(cc => c.Equals(cc))) returns true if every object in myWorkers collection is presented in Workers collection of other.
you can rewrite this expression with loops:
bool All = true;
foreach (var c in this.myWorkers)
{
bool Any = false;
foreach (var cc in other.Workers)
if (c.Equals(cc))
{
Any = true;
break;
}
if (!Any)
{
All = false;
break;
}
}
myResults.Add(All);
Adds a boolean to the list myResults indicating whether or not each item in myWorkers collection is reference equal to any item in other.Workers collection.
The same code can be rewritten without LINQ as:
var myResults = new List<bool>();
bool all = true;
foreach (var c in myWorkers)
{
bool any = false;
foreach (var cc in other.Workers)
{
if (c.Equals(cc))
{
any = true;
break;
}
}
if (!any)
{
all = false;
break;
}
}
myResults.Add(all);
Here we are use method syntax (Lamda expression)
Lamda expression work with collection.
myResults.Add(this.myWorkers.All(c => other.Workers.Any(cc => c.Equals(cc))));
Here
Add() method is used of add element into myResults collection.
All() method used for traverse all the element of collection.
Any() method used for match element with condition if any match found then its skip rremain elements.
Expression return true if any element in collection match with cc

LINQ query to update property of a list

I am trying to convert the following code to linq:
for (int i = 0; i < List.Count;i++ )
{
List[i].IsActive = false;
if(List[i].TestList != null)
{
for(int j = 0;j<List[i].TestList.Count;j++)
{
List[i].TestList[j].IsActive = false;
}
}
}
I tried the following query :
(from s in List select s).ToList().ForEach((s) =>
{
s.IsActive = false;
(from t in s.TestList where t != null select t).ToList().ForEach((t) =>
{
t.IsActive = false;
});
});
But i get an error when TestList is null in the list. I am not sure what I am doing wrong here.
If your original(no LINQ) code is worked.
Then you missed one line, which check for null of TestList before iterating items
(from s in List select s).ToList().ForEach((s) =>
{
s.IsActive = false;
if(s.TestList != null) //This check of TestList was missing
(from t in s.TestList where t != null select t).ToList().ForEach((t) =>
{
t.IsActive = false;
});
});
You are selecting lists that are null
where t == null
Should the condition be
where t != null
A simple approach. No need to check for null.
s.ForEach((x)=>
{
x.IsActive = false;
x.TestList.Foreach((t)=>{t.IsActive = false});
});
You don't necessarily need an inner loop since it looks like you're deactivating all nested TestList items. You can just have two separate loops:
foreach(var item in List)
item.IsActive = false;
foreach(var item in List.Where(x => x.TestList != null).SelectMany(x => x.TestList))
item.IsActive = false;
Note that SelectMany "flattens" the inner lists into a single IEnumerable<T>.

modify items in a Generic List

i cannot modify generic List with :
var x = (PaypalResponse)Session["PaypalResponse"]; // x.Response is my List
x.Response.ToList().Where(i => i.Id== 1).ForEach(s => s.Selected = true);
where am I doing wrong?
Thanks.
You could do this:
x.Response.Where(i => i.Id == 1).ToList().ForEach(s => s.Selected = true);
However, it's a bit of a waste of resources to construct a new list just for this one line of code. I'd recommend this instead:
foreach(var s in x.Response.Where(i => i.Id == 1))
{
s.Selected = true;
}
If you only want to update at most one item, you can do this instead:
var s = x.Response.FirstOrDefault(i => i.Id == 1);
if (s != null)
{
s.Selected = true;
}
And of course, if you know there will be one item to update, it's even easier:
x.Response.First(i => i.Id == 1).Selected = true;

Categories

Resources