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
Related
The GalleryDetail.Id is 148 when it enters the first foreach loop, and the GalleryDetail.Id is 148 when it enters the second foreach loop. But it does not enter the first foreach loop again. It continues from the second. How do I get it to re-enter the first loop here?
NOTE: I do not have direct access from the GalleryDetail.Id request.
var detailList = await _repo.GalleryDetail.GetAllAsync();
foreach (var item in mapped.GalleryDetails)
{
foreach (var item2 in detailList)
{
if (item.Id != item2.Id)
{
var mapped3 = _mapper.Map<GalleryDetails>(item2);
await _repo.GalleryDetail.DeleteAsync(mapped3);
}
}
}
It's not necessary to use 2 loop here, you can use LinQ instead. And you should not leave async Delete inside foreach loop, because it will connect to you database mutiple time. Example:
var detailList = await _repo.GalleryDetail.GetAllAsync();
//Map the whole list
var detailListMapped = _mapper.Map<List<GalleryDetails>>(detailList);
//Use LinQ to find database items not in request
var deletedList = detailListMapped.Where(x => !mapped.GalleryDetails.Any(y => y.Id == x.Id)).ToList();
//Entity Framework have RemoveRange function, you should use it here
await _repo.GalleryDetail.RemoveRangeAsync(deletedList );
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.
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)
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
I have two lists:
a. requestedAmenities
b. units with amenities.
I want to filter those units that have any one of the "requested amenities".
I have tried to achieve the same result using foreach loops but I believe it should be much easier using LINQ. Can someone please help\advice?
UnitAmenities unitSearchRequestAmenities = unitSearchRequest.Amenities;
var exactMatchApartmentsFilteredByAmenities= new Units();
IEnumerable<string> requestAmenitiesIds = unitSearchRequestAmenities.Select(element => element.ID);
foreach (var unitCounter in ExactMatchApartments)
{
IEnumerable<string> unitAmenities = unitCounter.Amenities.Select(element => element.ID);
foreach (var requestAmenityId in requestAmenitiesIds)
{
foreach (var unitAmenity in unitAmenities)
{
if (requestAmenityId == unitAmenity)
{
exactMatchApartmentsFilteredByAmenities.Add(unitCounter);
//break to the outmost foreach loop
}
}
}
}
You could filter based on compliance with an Intersect rule
var matchedAmenities = ExactMatchApartments.Where(ema => ema.Amenities
.Any(x => unitSearchRequestAmenities
.Count(y => y.ID == x.ID) == 1));
exactMatchApartmentsFilteredByAmenities.AddRange(matchedAmenities);
This is a somewhat "custom" Intersect given that the default LINQ Intersect extension doesn't support lambda expressions.
It's hard to tell from your types, but I think the following should do the trick
from unit in ExactMatchApartments
from amenity in unit.Amenities
join requestedAmenity in unitSearchRequestAmenities
on amenity.ID equals requestedAmenity.ID
select unit
This is a case where a query expression is both easier to read and understand as opposed to dot notation.
Thanks Jason, I believe it must be Intersect not Except.I have changed the code to the following:
var amenities = unitSearchRequest.Amenities;
if (amenities.Count > 0)
{
//filter the unit's amenities's id's with the search request amenities's ID's.
var exactMatchApartmentsFilteredByAmenities= new Units();
var requestAmenitiesIds = amenities.Select(element => element.ID);
foreach (var unitCounter in ExactMatchApartments)
{
var unitAmenities = unitCounter.Amenities.Select(element => element.ID);
var intersect =unitAmenities.Intersect(requestAmenitiesIds);
if (intersect.Any())
{
exactMatchApartmentsFilteredByAmenities.Add(unitCounter);
break;
}
}
}
I will test the code and update here my results.