Remove all null entries from generic list of Object - c#

This one should be easy, but seems to be eluding me.
Given this variable: (which contains ~30 records)
var seriesData = new List<List<object>>();
How do I loop through every record, and omit any record that contains a null, anywhere inside?
Typically, each list inside will look like one of the following:
["02/16/2019", 5, 7, 10]
["02/17/2019", 3, 15, 2]
and sometimes:
["02/18/2019", 5, {null}, 10]
This is what I have tried, but, it's not working:
foreach (List<object> row in seriesData)
{
if (row.Contains(null)) seriesData.Remove(row);
}
The result I'm ending up with is completely empty?

You can use RemoveAll which accepts predicate:
seriesData.RemoveAll(row => row.Any(x => x == null))

If you can use LINQ, this should be easy:
seriesData = seriesData
// filter the lists (x) where all items in them (y) are not null
.Where(x => x.All(y => y != null))
// and get the result
.ToList();

Without LinQ, you may do something like this:
int i = 0;
while (i < seriesData.Count)
{
if (seriesData[i].Contains(null))
{
seriesData.RemoveAt(i);
} else {
i++;
}
}
This may very well be the most performant solution and not require LinQ if you don't use it already. If, on the other hand, you already use LinQ, then style may be more important than performance.
As an exercise, I write a version that changes the order of entries but has a lower complexity. As stated by #Lee, the above code may have an O(n^2) complexity. Here is another version, maybe some benchmarking if performance is really important would help:
int i = 0, last;
while (i < seriesData.Count)
{
if (seriesData[i].Contains(null))
{
last = seriesData.Count - 1;
seriesData[i] = seriesData[last];
seriesData.RemoveAt(last);
} else {
i++;
}
}

There are many ways to skin a cat. Here is yet another one that doesn't modify your original list:
var nonulls = seriesData.Where(sd => !sd.Any(o => o == null));

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.

Get every nth element or last

I'm hitting a brick wall with this, and I just can't seem to wrap my head around it.
Given a List of objects, how can i get every third element starting from the end (so the third to last, sixth to last etc) but if it gets to the end and there are only 1 or 2 left, returns the first element.
I'm essentially trying to simulate drawing three cards from the Stock and checking for valid moves in a game of patience, but for some reason i'm struggling with this one concept.
EDIT:
So far I've tried looked into using the standard for loop increasing the step. That leads me to the second need which is to get the first element if there are less than three on the final loop.
I've tried other suggestions on stack overflow for getting nth element from a list, however they all also don't provide the second requirement.
Not entirely sure what code i could post that wouldn't be a simple for loop. as my problem is the logic for the code, not the code itself.
For example:
Given the list
1,2,3,4,5,6,7,8,9,10
i would like to get a list with
8, 5, 2, 1
as the return.
pseudocode:
List<object> filtered = new List<object>();
List<object> reversedList = myList.Reverse();
if(reversedList.Count % 3 != 0)
{
return reversedList.Last();
}
else
{
for(int i = 3; i < reversedList.Count; i = i +3)
{
filterList.Add(reversedList[i]);
}
if(!filterList.Contains(reversedList.Last())
{
filterList.Add(reversedList.Last());
}
Try using this code -
List<int> list = new List<int>();
List<int> resultList = new List<int>();
int count = 1;
for (;count<=20;count++) {
list.Add(count);
}
for (count=list.Count-3;count>=0;count-=3)
{
Debug.Log(list[count]);
resultList.Add(list[count]);
}
if(list.Count % 3 > 0)
{
Debug.Log(list[0]);
resultList.Add(list[0]);
}
Had to try and do it with linq.
Not sure if it live up to your requirements but works with your example.
var list = Enumerable.Range(1, 10).ToList();
//Start with reversing the order.
var result = list.OrderByDescending(x => x)
//Run a select overload with index so we can use position
.Select((number, index) => new { number, index })
//Only include items that are in the right intervals OR is the last item
.Where(x => ((x.index + 1) % 3 == 0) || x.index == list.Count() - 1)
//Select only the number to get rid of the index.
.Select(x => x.number)
.ToList();
Assert.AreEqual(8, result[0]);
Assert.AreEqual(5, result[1]);
Assert.AreEqual(2, result[2]);
Assert.AreEqual(1, result[3]);

c# Linq - Check for multiple in list

I have a list of transactions and i need to find if there is more then 1 account
i did
var MultipleAccounts = list.GroupBy(t => t.AccountId).Count() > 1;
is there a better way?
If you're willing to lose the single-line I prefer the use of !.All(item => bool) or .Any(item => bool) as I think it's the most semantic and easiest to read, as well as being a good candidate for the fastest.
var accountId = accounts[0].AccountId;
var hasMultipleAccounts = !accounts.All(account => account.AccountId == accountId);
Alternatively, and perhaps even more semantically, you could use .Any(item => bool) instead of .All(item => bool).
var accountId = accounts[0].AccountId;
var hasMultipleAccounts = accounts.Any(account => account.AccountId != accountId);
Things to watch out for are making sure you have at least one item (so that accounts[0] doesn't fail) and not doing a multiple enumeration of your IEnumerable. You say you're working with a List, so multiple enumeration shouldn't cause you any trouble, but when you just have an unknown IEnumerable it's important to be careful.
I prefer:
var MultipleAccounts = list.Select(t => t.AccountId).Distinct().Skip(1).Any();
This should be exceedingly fast as it will stop iterating the source list as soon as it finds a second AccountId.
Anytime you execute a full .Count() it has to iterate the full source list.
You can test this with the following code:
void Main()
{
Console.WriteLine(Data().Select(t => t).Distinct().Skip(1).Any());
}
private Random __random = new Random();
public IEnumerable<int> Data()
{
while (true)
{
var #return = __random.Next(0, 10);
Console.WriteLine(#return);
yield return #return;
}
}
A typical run looks like this:
7
9
True
Ok here is what i found the quickest
public bool HasMultipleAccounts(List<Account> list)
{
foreach (var account in list)
if (account.AccountId != list[0].AccountId)
return true;
return false;
}
usage: var MultipleAccounts = HasMultipleAccounts(list);
Credits: #hvd
i know its more code but if you think what the cpu needs to do its the quickest

Elegant way to retrieve the index of the max element from a List in C#

I am discovering the tools C# provides to work with collections.
Suppose I have a List of elements, and I want to retrieve the one that most satisfies a property. Basically a elements.Max(predicate), except that I am interested in the index of the best element. The reason I want the index and not the element itself is there might not be such element, and the type is non-nullable.
Writing a function doing this is trivial, but I am interested in using the expressiveness of the tools C# provides to get a solution that is both concise, clear, and optimal (O(n)).
At this point I have the following code, which still looks cluttered, and evaluates the property twice.
List<foo> elements;
private int getBest(object x)
{
var indices = Enumerable.Range(0, elements.Count);
return indices.Aggregate(-1, (best, next) =>
(-1 == best || eval(x, elements[next]) > eval(x, elements[best])) ? next : best);
}
How can I make this piece of code better?
Addendum: I didn't put it in the code for the sake of clarity, but if eval() is below a certain threshold the element is discarded.
I'd recommend using the Select in conjunction with Aggregate LINQ extension methods. Using the Select method you can create an anonymous type which houses the index and value of each item within your collection. Then using the LINQ Aggregate method, you can narrow down the item with the greatest value. Some like this should work I think:
private int GetIndexOfHighestValue(IEnumerable<int> list)
{
return list.Select((i, v) => new { Index = i, Value = v })
.Aggregate((a, b) => (a.Value > b.Value) ? a : b)
.Index;
}
Doing it with LINQ is fun, but doing it without LINQ makes it more intuitive:
int bestIndex = -1;
int bestResult = -1;
for(int i = 0; i < elements.Count; ++i)
{
int currentResult = eval(x, elements[i]);
if (currentResult > bestResult)
{
bestResult = currentResult;
bestIndex = i;
}
}
Something like this could work:
// OOPS: This won't work because Max is defined the way it is. Always bugged me...
var result = elements.Select((e, i) => new {Element = e, Index = i}).Max(x => x.Element).Select(x => x.Index);
oh rats. Right. This will not work. So: Let's pull out our allrounder: Aggregate. Here we go:
var elements = new List<int>{1, 7, 2, 5};
var result = elements.Select((e, i) => new {Element = e, Index = i})
.Aggregate(
new { Element = elements.First(), Index = -1}, // gotta start somewhere and Element is non-nullable according to OP
(max, next) => (max.Element > next.Element) && max.Index >= 0 ? max : next,
max => max.Index);
This results in 1. Did that help?

How to make these foreach loops efficient?

I have List of (PatchFacilityManager) and a List of (Int) facilityManagerId. I want to make the below code efficient. Is there any way to remove these two foreach loop.
foreach (PatchFacilityManager PM in patchFacilityManager)
{
foreach (int FM in facilityManagerId)
{
if (PM.FacilityManagerId == FM)
{
PM.IsSelected = true;
}
}
}
Here's one way,
foreach (PatchFacilityManager PM in patchFacilityManager)
{
PM.IsSelected = facilityManagerId.Contains(PM.FacilityManagerId);
}
EDIT
This solution is efficient in two three ways IMHO as compared to the code given in the question.
First, it does not test for the condition and the result of the expression is straight away assigned into PM.IsSelected. As per LukeH's comment, it is mandatory to not set the PM.IsSelected to false, so the condition is unavoidable. However this improvement is applicable if the asked needs to set it to false. . From question asker's comment, his case seem to go right with this optimization. So no need for conditional assignment.
Second, it does not iterate through whole list, since List.Contains(int), returns true and come out of loop on the first occurrence of the int passed in argument.
Third, when framework gives you the functionality List.Contains(int), then why re-invent the wheel. So from maintenance perspective this is also more efficient.
var ids = new HashSet<int>(facilityManagerId);
foreach (PatchFacilityManager pfm in patchFacilityManager)
{
if (ids.Contains(pfm.FacilityManagerId))
pfm.IsSelected = true;
}
patchFacilityManager
.Where(c => facilityManagerId.Contains(c.FacilityManagerId))
.ForEach(c => c.IsSelected = true);
You could store the facility manager id's in an array in sorted order, and then look them up using BinarySearch instead of a foreach.
patchFacilityManager
.Where(m => facilityManagerId.Contains(m.FacilityManagerId))
.ToList()
.ForEach(m => m.IsSelected = true);
or
patchFacilityManager
.Join(facilityManagerId, m => m.FacilityManagerId, f => f, (m,f) => m)
.ToList()
.ForEach(m => m.IsSelected = true);
Another variant using LINQ syntax:
var match = for PM in patchFacilityManager
join FM in facilityManager on PM.FacilityManagerId equals FM
select PM;
foreach(var PM in match)
{
PM.IsSelected = true;
}
Just for fun and outside the box - an approach when the lists are sorted:
static void Main(string[] args)
{
List<int> nums = new List<int>(new int[] { 1, 2, 3, 4, 5, 6, 7, 8 });
List<int> ids = new List<int>(new int[] { 2, 4, 5 });
for (int i = 0, j = 0; i < nums.Count && j < ids.Count; i++)
{
int num = nums[i];
int id = ids[j];
if (num == id)
{
Console.WriteLine("Match = " + id);
j++;
}
}
Console.ReadLine();
}
I presume no knowledge of the performance benefit or detrement of this idea. Of course you can modify this to use a main foreach for the numbers and you manually use the Enumerator of the IDs inside the main foreach if you have allergies to fors.

Categories