Why is this C# code not functioning as expected? - c#

This is a C# code to enter 10 names, sort them in ascending order and print them with the condition if either of the elements are "Nobody" or "Somebody" it should skip that name when printing. The issue with my code is that it doesn't skip those 2 mentioned words and prints them as well, I don't understand why.
Sample Input:
Ravi
Somebody
Tanvir
Ramesh
Nobody
Ani
Nobody
Vishwanath
Somebody
Nitin
Sample Output:
Ani
Nitin
Ramesh
Ravi
Tanvir
Vishwanath
using System;
using System.Collections;
namespace LearnCsharp
{
class NamesWithArrayList
{
public static void Main(string[] args)
{
//Update the code below
ArrayList alObj;
alObj = new ArrayList();
int max=10;
string item="";
for(int i=0;i<max;i++)
{
alObj.Add(Console.ReadLine());
}
alObj.Sort();
foreach (string item1 in alObj)
{
if(alObj.Contains("Somebody")){}
else if(alObj.Contains("Nobody")){}
else
Console.WriteLine(item1);
}
}
}
}

You're checking if the list contains "Somebody" each time rather than if the current value is "Somebody".
ArrayList is essentially a collection of objects, so string comparison is not being used when calling Contains. It's instead using object reference comparison, and since your inputs are different objects that the string constants, the comparison always fails.
So a version which uses string comparisons would be:
foreach (string item1 in alObj)
{
if(item1 == "Somebody"){}
else if(item1 == "Nobody"){}
else
Console.WriteLine(item1);
}
which could be simplified as:
foreach (string item1 in alObj)
{
if(item1 != "Somebody" && item1 != "Nobody")
Console.WriteLine(item1);
}

You need to check '''item1''' if it is your searched string and you need to put some code for the if statement what your app should do than.

The problem is that on the if(alObj.Contains("Somebody")){} line you're asking if the original alObj ArrayList contains the string "Somebody". Your foreach loop should be re-written as follows:
foreach (string item1 in alObj)
{
if(item1 == "Somebody")
{
}
else if (item1 == "Nobody")
{
}
else
Console.WriteLine(item1);
}
However this could be improved further by merging those the "Somebody" and "Nobody" cases into one:
foreach (string item1 in alObj)
{
if(item1 == "Somebody" || item1 == "Nobody")
{
// Do nothing
}
else
Console.WriteLine(item1);
}
Finally this is a bit weird looking, the real intent of the code is "Only output the item if its not equal to 'Somebody' and not equal to 'Nobody'", which is best expressed like so:
foreach (string item1 in alObj)
{
if(item1 != "Somebody" && item1 != "Nobody")
Console.WriteLine(item1);
}

In 2020 you don't need to use ArrayList. In fact the only reason it is still exists probably is backward compatibility. Use List<string>. If you do that, you can do this
using System.Linq;
myList.Where(x => x != "Nobody" && x != "Somebody")
.Sort(StringComparer.OrdinalIgnoreCase)
.ToList()
.ForEach(x => Console.WriteLine(x));
Another interesting way is this
using System.Linq;
var checkList = new List<string>(){ "Nobody", "Somebody" };
myList.Except(checkList)
.Sort(StringComparer.OrdinalIgnoreCase)
.ToList()
.ForEach(x => Console.WriteLine(x));
In this instance you work on the lever of 2 lists. You will retrieve only items that don't match.

Related

Listbox Auto sort numbers but exclude string

I'm trying to make a leaderboard in Windows forms using c# but I can't come up with a solution.
Here is my current code.
lstleaderboard.Items.Add(int.Parse(txtScore.Text));
ArrayList Sorting = new ArrayList();
foreach (var o in lstleaderboard.Items)
{
Sorting.Add(o);
}
Sorting.Sort(new ReverseSort());
lstleaderboard.Items.Clear();
foreach (var o in Sorting)
{
lstleaderboard.Items.Add(o);
}
And I tried altering the code like this:
lstleaderboard.Items.Add(int.Parse(txtScore.Text));
ArrayList Sorting = new ArrayList();
foreach (var o in lstleaderboard.Items)
{
Sorting.Add(o);
}
Sorting.Sort(new ReverseSort());
lstleaderboard.Items.Clear();
foreach (var o in Sorting)
{
lstleaderboard.Items.Add(o + txtName.Text );
}
if (lstleaderboard.Items.Count == 11)
{
lstleaderboard.Items.RemoveAt(lstleaderboard.Items.Count - 1);
}
but this did not really work since it was then also sorting the names which messed up the scoreboard and it showed the wrong results, number one could be number three for example.
I am still learning so I apologize if my question is silly or my code is "weird"
Thanks
also here is my sorting class:
public class ReverseSort : IComparer
{
public int Compare(object x, object y)
{
return Comparer.Default.Compare(y, x);
}
}
So lstLeaderBoard is just list of integers, so you can safely use integer comparison, the way you did it, it just uses some default comparer on object type, which is far away from integer.
So simple LINQ OrderBy would suffice:
lstLeaderBoard.Items.Add(int.Parse(txtScore.Text));
var sortedItems = lstLeaderBoard.Items.Cast<int>().OrderBy(x => x);
lstLeaderBoard.Items.Clear();
foreach (var item in sortedItems)
lstLeaderBoard.Items.Add(item);

How to check if there are no matching elements in two lists of objects?

I have two lists of objects: List<One> & List<Two> and I want to achieve the following.
For each One.Id:
if there's a matching Id in Two.Id, do nothing
else if there are no matching Ids in Two.Id, do something
I tried with iteration inside iteration, but couldn't find a suitable solution.
Thanks in advance.
You can use LINQ's Any() method to check for presence of matching items:
foreach (var one in listOne) {
if (!listTwo.Any(two => two.Id == one.Id)) {
// No matches
}
}
I would use Any() in a dedicated function like this:
bool HasMatches(List<One> ones, List<Two> twos)
{
foreach (var item in ones) {
if (!twos.Any(other => other.Id == item.Id))
return false;
}
return true;
}
You should finish the search once you have found no match for the current item.
foreach(var item1 in list1)
{
if(list2.Any(item2=>item2.Id== item1.Id))
{
//do nothing
continue;
}
// do something
}
you can do some thing like this
List<one> a = new List<one>;
List<two> b = new List<two>;
foreach( l in a)
{
if(b.Any(x=>x.id == l.id)){
//do something
}
else{
//do something
}
}
You can use Contains like this.
bool isContainId = List<one>.Contains(two.Id);
if (isContainsId)
{
// do something
}
else
{
// do something
}

Avoid Multiple Nested For Each Loops

I am new to C# and taking my first course at a University. I think this is an issue with instantiation but here goes with what I am after.
I am wanting to get the value from te.ED w/o having to go through the multiple for each loops, as if the answer is "No" then there is no need to go through the loops and extract multiple data elements (not being shown). Is there a way to check that value BEFORE going through all of the for each loops?
Code is here
TR reply = service.track(request);
foreach (CTD ctd in reply.CTD)
{
foreach (TD td in ctd.TD)
{
if (td.Events != null)
{
foreach (TE te in td.Events)
{
if (te.TimestampSpecified)
{
//This is where the field I am after exists
if (te.ED == "YES")
Console.WriteLine("The answer is yes");
else
Console.WriteLine("The answer is no");
}
}
}
}
}
Per the comment from #Anis Programmer - I believe you are wanting to see the TD element from the class CTD. If that is the case - see below
[System.Xml.Serialization.XmlElementAttribute("TD")]
public TD[] TD {
get { return this.tdf; }
set { this.tdf = value; }
}
Per the answer from #Neel below - I am very close with the syntax
var result = reply.CTD.SelectMany(c1 => c1.TD)
.SelectMany(c2 => c2.Events.Select(c3 => c3.TimestampSpecified));
foreach (var ltr in result)
Console.WriteLine(ltr)
Now the issue is that the foreach loop makes two passes, and the value returned from both is True
What do I need to change in this syntax?
I think you can use a LinQ in a foreach like this:
foreach (
var te in from ctd in reply.CTD
from td in ctd.TD
where td.Events != null
from te in td.Events
where te.TimestampSpecified
select te)
{
Console.WriteLine(te.ED == "YES" ? "The answer is yes" : "The answer is no");
}
What I assumed from the example you have posted that you want to avoid multiple nested foreach loop.
You can use linq to shorten the same.Here is how using lamda expression.
var result = reply
.SelectMany(c1 => c1.CTD)
.SelectMany(c2 => c2.TD)
.SelectMany(c3 => c3.Events.Select(c4 => c4.TimestampSpecified));
Now you just loop on the result and compare with ED value.

If you never hit this line of code, do something

Okay, well let's say I have a variable list of items. It can be any number of items. Each item can be either 0,1,2,3, or 4. So I make a loop.
foreach(item in allitems)
{
if (item == 0) continue;
do stuff for items 1-4.
}
Let's say that every single item it goes through is 0. Well what if I want to execute a specific line of code in that case? Of course I could do something like
int count = 0
foreach(item in allitems)
{
if (item == 0) {count++; continue;}
do stuff for items 1-4.
}
if(count == allitems.Count())
{
do stuff
}
But I always felt cheap using count variables to do something like this. Is there any thing I can do that doesn't feel like duct-taping a solution together?
There's no need to use a count here - just keep a flag which is set if you get past the check, rather than within the check:
bool anyNonZeroItems = false;
foreach(item in allitems)
{
if (item == 0)
{
continue;
}
anyNonZeroItems = true;
// Whatever else
}
if (!anyNonZeroItems)
{
// Everything was 0 (or the collection was empty)
}
You can use Enumerable.All to check if all items in a List satisfy a condition.
in this case something like
if (allItems.All(i => i == 0) {
//do stuff
}
Incidentally in your example you have (if item = 0) and this should be if (item == 0)
What you currently have is perfectly acceptable. I use that kind of pattern all the time.
One thing I would suggest is making count into a bool unless there's an actually a difference between when count == 11 andcount > 1`
This is a pretty common problem, but you propose an odd solution. Why not just use a boolean to indicate state?
bool branchExecuted = false;
foreach(item in allitems)
{
if (item == 0)
{
branchExecuted = true;
continue;
}
//do stuff for items 1-4.
}
if(!branchExecuted)
{
//do stuff if we never hit that line
}
Using this instead of a LINQ / convenience function to operate on the list will only cost you a single boolean and you only have to iterate over your list once.

If ListView Column "x" Contains "value"

This is embedded in another loop, and well, it's pretty slow. Is there a better way to do this?
for(int i=0;i< listView.Items.Count;i++)
{
if(listView.Items[i].SubItems[3].Text == "asdf")
{
}
}
Well there's a nicer way to do it:
foreach (ListViewItem item in listView.Items)
{
if (item.SubItems[3].Text == "asdf")
{
...
}
}
Or you could use LINQ:
var query = listView.Items
.Cast<ListViewItem>()
.Where(item => item.SubItems[3].Text == "asdf");
foreach (var item in query)
{
...
}
I doubt that that will be faster though...
Does your outer loop change the listView? If not, could you do the query once and reuse the results in the outer loop?
In case someone runs across this using WPF, you don't get .SubItems on item when you use foreach (ListViewItem item in listView.Items). Instead, I found I could just use DataRowView and get the value of the cell that way:
foreach (DataRowView drv in listView.Items)
{
if (drv.Row[3].ToString() == "asdf")
{
...
}
}
You do have to add a using System.Data; statement at the top of your class to use it. I found it worked in WPF, and it might in other areas (i.e. WinForms), as well.

Categories