LINQ Intersect but add the result to a New List - c#

I have a list called DiscountableObject. Each item on the list in turn has a Discounts collection. What I need is a list of Discounts which are common across all DiscoubtableObjects.
Code:
List<Discount> IntersectionOfDiscounts = new List<Discount>();
foreach(var discountableItem in DiscoutableObject)
{
IntersectionOfDiscounts = IntersectionOfDiscounts.Intersect(discountableItem.Discounts);
}
This will undoubtedly return an empty list because by IntersectionOfDiscounts was empty in the first instance.
What I want is to take item 1 of the DiscountableObject, compare it with the next item of DiscountableObject and so on.
I know what I am trying to do is wrong because, I am doing the intersection and the addition to the list at the same time...but how else baffles me?
How do I get around this?

Initialize IntersectionOfDiscounts to the first Discount List (if there is more than one) rather than to an empty list. You can also then skip the first item in the 'foreach' loop.
// add check to ensure at least 1 item.
List<Discount> IntersectionOfDiscounts = DiscoutableObject.First().Discounts;
foreach(var discountableItem in DiscoutableObject.Skip(1))
{
IntersectionOfDiscounts = IntersectionOfDiscounts.Intersect(discountableItem.Discounts);
}

Possibly more elegant way:
var intersection = discountableObject
.Select(discountableItem => discountableItem.Discounts)
.Aggregate( (current, next) => current.Intersect(next).ToList());
Missed your 6 minute deadline, but I like it anyway...

Related

Retrieve the previous indexed record in a list, if exists

I am using .NET 3.5 . My requirement is to traverse through a list of objects ordered descending by date, find a match for a particular record, capture that object and then, IF a record exists on a date prior to that, which means the captured object's index minus one (if exists), capture this object too, which means my output list can have either one record or two records depending on whether there was a previous dated record or not. Is there a clean way of achieving this ?
I tried capturing index of matched record and going for previous index by adding -1 to index >> Risk of index out of bounds exception if the previous element does not exist.
How can I avoid the index out of bound exception, yet check for the existence of previous element , if exists ? I am sure there is a much cleaner way of doing it rather than the way I am trying. So I am reaching out to you to advice if there is a nicer way of doing this....
Any advise is highly appreciated. Thank you
Take a look at the below. Object is just a datetimeoffset, but should illustrate LINQ query. You are looking for .Top(2) (this could be more complicated if you need this grouped by something):
LinqPad sample below, but should be easily pasted into console app.
void Main()
{
var threeItems = new List<DateTimeOffset>(new[] { DateTimeOffset.Now, DateTimeOffset.Now.AddDays(-1), DateTimeOffset.Now.AddDays(-2) });
var twoItems = new List<DateTimeOffset>(new[] { DateTimeOffset.Now, DateTimeOffset.Now.AddDays(-1) });
var oneItem = new List<DateTimeOffset>(new[] { DateTimeOffset.Now });
ShowItems(GetItems(threeItems));
ShowItems(GetItems(twoItems));
ShowItems(GetItems(oneItem));
}
IEnumerable<DateTimeOffset> GetItems(List<DateTimeOffset> items)
{
return items
.OrderByDescending(i => i)
.Select(i => i)
.Take(2);
}
void ShowItems(IEnumerable<DateTimeOffset> items)
{
Console.WriteLine("List of Items:");
foreach (var item in items)
{
Console.WriteLine(item);
}
}
I think what you are looking for will require using the List.IndexOf to find the index of the matching item, then retreive the previous item if there is a datetime before the date you have searched for. My example here uses an object called listObject which contains a datetime as well as other properties;
DateTime searchDate = DateTime.Parse("26/01/2019");
var orderedList = listObjects.OrderBy(x => x.DateProperty).ToList();
listObject matchingItem = orderedList.First(x => x.DateProperty.Date == searchDate.Date); //gets the first matching date
listObject previousMatching = orderedList.Any(x => x.DateProperty.Date < searchDate.Date) ? orderedList[orderedList.IndexOf(matchingItem) - 1] : null; //returns previous if existing, else returns null

For each loop takes more time to complete

I have a foreach on a list. List length will be huge always like 200K or so.
When i iterate through the list in foreach, the logic within the foreach will be dealing with another collection which is also will be around 1 million items in the list. For each iteration, the collection will be filtered and it needs to update on property and return the collection as it is. But by doing this, the process never gets completed.
foreach(var list in iterationlist)
{
var filteredCollections = collection.where(a=>a.name==list.name);
filteredCollections.foreach(x=>{x.city="xxxx";});
}
What are the ways to make this logic faster ? Currently this implementation takes more than 3 hours but not completing yet
You can use a Lookup (it's similar to Dictionary<key, List<value>>):
var lookup = collection.ToLookup(a => a.name);
foreach (var list in iterationlist)
foreach (var x in lookup[list.name])
x.city="xxxx";
Another option is to hash the names:
var names = new HashSet<string>(iterationlist.Select(x => x.name));
foreach (var x in collection)
if (names.Contains(x.name))
x.city="xxxx";

Add element to list before specific element

I have a list of items, lets say 100 items. I need to add another element before the existing element that matches my condition. What is the fastest way and the most performance optimized to do this?
ie.:
foreach (var i in myList)
{
if (myList[i].value == "myValue")
{
myList[i-1] add ("someOtherValue")
}
}
Maybe i should use other container?
First you could find the index of your item using FindIndex method:
var index = myList.FindIndex(x => x.value == "myvalue");
Then Insert at the right point:
myList.Insert(index,newItem);
Note that inserting at a given index pushes everything else forward (think about finding your item at index 0).
Consider using a LinkedList<T>. It has the advantage that inserting or removing items does not require shifting any items. The disadvantage is that items cannot be accessed randomly. You have to traverse the list starting at the first or last item in order to access the items.
myList.Insert(myList.IndexOf("myValue") - 1, "someOtherValue");
You should probably check to make sure myvalue exists first, and it is not in index 0.
int index = myList.IndexOf("myValue");
if (index >= 0)
myList.Insert(index, "myNewValue");
By the way, you should not modify your own collection or list while iterating with for-each (as in your code above).
I presume the list is an array - in which case have you tried doing this with Linq?
string[] mylist = new string[100];
// init the list
List<string> list = keys.ToList();
list.Insert(1,"somethingelse");
mylist = list.ToArray(); // convert back to array if required
if it is a List to begin with, you can skip the conversions and use Insert directly.

collection help please

I have a collection< of int's >.
1
2
3
When i remove, for example, the 2. The collection becomes 1,3.
However, when I go to add another item the list becomes
1
3
3
Because the sequence is based off the count of items in collection
is there an easy way to resequence. The example above where i'm showing 1,3 should be resequence to 1,2 and then the next new item will be a 3.
It sounds like your list can be replaced with a integer variable, since all you are "storing" here is the (1-based) index of the item in the list. Creating a "list" from 1 to n would just be int count = n. Testing if an item is in the list becomes if (item <= count). Adding an item would be count++, and removing count--.
You could rewrite your list every time you remove an object using Enumerable.Range, so the code to you would be something like this:
//execute this after remove element
void Resequence()
{
myList = Enumerable.Range(1, myList.Count).ToList();
}
so the add will be something like this
void AddElement(){
myList.Add(myList.Count + 1);
}
Note: in the add element you should remove the +1 if the list is zeroBased.

Is it possible to let List collapse after I do removeAt certain times?

Let's say there is a list of List<UInt32>
Thus, :
12|12
23|33
33|22
11|22
I need to remove 0th and 2nd element (List<UInt32>). However, when I try to foreach this list and first remove 0th, the List collapses its elements and 1st becomes 0th now.. so I don't want to delete the wrong element, because my another List<int> includes the positions of elements I want to delete.
Anyway, I thought of doing some algorithm for this, but I wonder if there is already solution for this problem.
Sort the positions list in descending order and remove elements in that order.
foreach (var position in positions.OrderByDescending(x=>x))
list.RemoveAt(position);
Are you able to tell which elements you need to remove based on the elements themselves instead of the index? If so, and you want to change the existing list instead of creating a newly filtered one, use:
list.RemoveAll(x => (whatever - a predicate));
This is more efficient than removing elements one at a time - each element is only moved once. It's also clearer, if the predicate is obvious :)
If you are using C#3.0 (or greater) you can use the LINQ extension method Where to filter your list.
var source = new List<int> { 1, 2, 3, 4, 5 };
var filter = new List<int> { 0, 2 };
var result = source.Where((n, i) => !filter.Contains(i)) // 2, 4, 5
And if you want it back to a List<int> instead of IEnumerable<int>, just use ToList()
var list = result.ToList();
You could create a new collection and adding the items that you don't want to remove and then assign the new collection to the original. That may even be faster than RemoveAt if the indices to be removed are in a hashset/dictionary rather than a list.

Categories