C# - check if substring is present in arraylist of strings - c#

Suppose I've an Arraylist(arr1) like the below
"String1 is present"
"String2 is present"
"String3 is present"
i wanted to see if 'String2' is present in this arraylist. i've done something like the below:
var containsstringmatch = arr1.OfType<string>().Any(arg=>arg.Contains("String2"));
if (containsstringmatch==true)
{
IEnumerable v1 = arr1.OfType<string>().Where(arg=>arg.Contains("String2"));
foreach (string s in v1)
{
st1 = s;
}
Console.WriteLine(st1);
}
which gives me the below output which is good:
"String2 is present"
I wanted to see if this can be achieved without me using the foreach loop. Can someone please provide suggestions as to how to do it.
Thanks

If you want only to print the first string that contains the search, you can use FirstOrDefault():
var foundString = arr1.OfType<string>().FirstOrDefault(arg => arg.Contains("String2"));
Console.WriteLine(string.IsNullOrEmpty(foundString) ? "Not found" : foundString);
Also, as Aomine wrote in his answer - ArrayLists where good when we worked with .Net 1.1. Since .Net 2.0 introduced generics, ArrayLists should be avoided.
As Rufus L wrote in his comment, your current code gets the last string containing the search string, not the first. If you want the last and not the first, you can simply use LastOrDefault instead of FirstOrDefault.

I'd avoid using ArrayList in this day and age in .NET, instead, favor the List<T> (if possible).
As for:
I wanted to see if this can be achieved without me using the foreach
loop.
if by this you mean that you want to avoid the foreach construct and perform everything inline:
arr1.OfType<string>()
.Where(arg => arg.Contains("String2"))
.ToList()
.ForEach(s => Console.WriteLine(s));
or if you just want to find the last element satisfying the said criteria:
var result = arr1.OfType<string>().LastOrDefault(arg => arg.Contains("String2"));

There is no way to do this without a foreach or for loop. But you can create an extension method that will move the code out of your method.
public static class ConsoleExtensions
{
public static void WriteToConsole(this IEnumerable<string> list)
{
foreach (string item in list)
Console.WriteLine(item);
}
}
usage:
arr1.OfType<string>().Where(arg=>arg.Contains("String2")).WriteToConsole();

Related

C# Linq OrderBy based on condition

I have a foreach loop that I want to change:
foreach (var line in lines.OrderBy(x=> x.ColA))
If a condition is met, then instead of ordering by ColA, I want to order by ColB.
I know this could be done like the following:
var orderLines = new List<OrderLines>();
if (condition)
orderLines = lines.OrderBy(x => x.ColB).ToList();
else
orderLines = lines.OrderBy(x => x.ColA).ToList(); ;
foreach (var line in orderLines)
But I am sure there is a more elegant solution.
Several solutions.
(1) Don't do the ToList() before your foreach, only create the IEnumerable.
IEnumerable<OrderLines> orderLines = condition ?
lines.OrderBy(orderLine => orderLine.ColB) :
lines.OrderBy(orderLine => orderLine.ColA);
foreach(OrderLine orderlLine in orderLines) {...}
(2) If you will be using this on several locations, consider to create an extension method. This way your method looks like any other LINQ method.
See extension methods demystified
public static IEnumerable<OrderLine> OrderBy(
this IEnumerable<OrderLine> source,
bool condition)
{
return condition ?
lines.OrderBy(orderLine => orderLine.ColB) :
lines.OrderBy(orderLine => orderLine.ColA);
}
Usage:
If operator checks chexBox1, sort by colB, else sort by colA:
IEnumerable<OrderLine> lines = ...
foreach(var sortedOrderLine in lines.OrderBy(this.CheckBox1.IsChecked))
{
...
}
Because it is an extension method of IEnumerable<OrderLine>, you can even intertwine it with other LINQ methods:
var result = lines.Where(orderLine => orderLine.Date.Year >= 2020)
.OrderBy(this.checkBox1.IsChecked)
.Select(orderLine => new
{
Id = orderLine.Id,
Price = orderLine.Price,
});
But all in all, it doesn't save you a lot of code. The only advantage would be if you would use it in a lot of methods. In that case, a change in how you want to OrderBy condition would have to be changed in only one place. But again: if you expect to use it in one place, moving it to a separate method might not help readers to understand what happens.
That is probably about as good as it gets.
Remember that behind that lambda expression magic happens which (effectively) binds to a Comparer<T> where T depends on the type of the columns being compared.
To make this more terse might make it less efficient. Specifically converting and comparing strings makes it both slower and can get you into trouble (ints sort to 1,2,3,...10,11,... vs their strings to "1","10","11",..."19","2","20","21"...).
A "one-liner" is only elegant if it's behaviour is obvious, otherwise it is obfuscated.
Your code is fine. (IMO;-)
Install NuGet System.Linq.Dynamic and you can pass property name as string to OrderBy like below.
Usage list.AsQueryable().OrderBy("PropertyName1 SortOrder, ropertyName SortOrder"). Where PropertyName will be ColA ColB. And SortOrder will be ASC DESC.
Add using System.Linq.Dynamic;
foreach (var line in lines.AsQueryable().OrderBy(condition ? "ColB" : "ColA")
For .Net Core install NuGet System.Linq.Dynamic.Core.
Add using System.Linq.Dynamic.Core;
foreach (var line in lines.AsQueryable().OrderBy(condition ? "ColB" : "ColA")
For a better practice rather than providing PropertyName as string use nameof(Class.Property) like in your case nameof(OrderLines.ColA). So in case you change ColA property it will show Builderror and you will not get run time exception.
foreach (var line in lines.AsQueryable().OrderBy(condition ? nameof(OrderLines.ColB) : nameof(OrderLines.ColA))
As #AlanK have mentioned, the closest we can get for simplifying OrderBy would be something like:
Func<OrderLines, string> selector = (orderLine) => condition ? orderLine.ColB : orderLine.ColA;
List<OrderLines> orderLines = lines.OrderBy(selector);
provided both ColA and ColB are of same data type. Otherwise it wouldn't be efficient due to the overhead of data type conversion.

Iterating through two Lists and checking the item value of the first list to the item values of the second list with a lambda expression

So in the program I'm trying to run I receive two lists, one with objects that contain an id in string format (looks something like "bb_b1203322") and one list with the id's(which in this place is only named "b1203322" for reasons unknown) and a description of the actually id's meaning.
var forms = await _tRepository.GetAllFormsAsync(lastUpdate);
var formDefinitions = await _deRepository.GetAllFormDefintionsAsync();
foreach (var form in forms)
{
foreach (var def in formDefinitions)
{
if (form.SetupFormName.Contains(def.BLKID))
form.SetupFormName = def.DESCR;
}
}
return forms;
Now this piece of code does exactly what I want it to, but I'd rather have it as a lambda expression because ... reasons :)
Now I've tried several different things but with my current knowledge of lambda expressions I can't get it to work.
Try this code. Note that you can use it if formDefinitions with suitable DESCR always exists.
forms.ForEach(f => f.SetupFormName = formDefinitions.FirstOrDefault(fd =>
f.SetupFormName.Contains(fd.DESCR)).DESCR);
This code uses a bit of LINQ to find the definition:
foreach(var form in forms)
{
var def = formDefinitions.FirstOrDefault(x => form.SetupFormName.Contains(x.DESCR));
if(def != null)
form.SetupFormName = def.DESCR
}
As you can see, it's not really saving all that much code.
Please note:
As Jon correctly comments, the behavior of this code is a bit different from your original one. This code uses the first occurrence if there are multiple and your code uses the last occurrence.
If this is actually a use case for your code, replace FirstOrDefault with LastOrDefault.
Extending the code above, you can do something like this:
foreach(var tuple in forms.Select(x => new { Form = x,
Definition =
formDefinitions.FirstOrDefault(y =>
x.SetupFormName.Contains(y.DESCR)) })
.Where(x => x.Definition != null))
{
tuple.Form.SetupFormName = tuple.Definition.DESCR;
}
But as you can see, this gets messy real quick.

C# modify list of string without iterating

I have a List which is returned to me from a 3rd party. Each string in the list ends in "mph" and I would like to remove the "mph". The obvious answer is to foreach over the list but I didn't know if there was a more efficient way to do it.
Thanks.
in a word, no. Something has to pass over the list in order to modify it. A for loop is probably the most efficient way though not necessarily the most concise
You have to iterate over the list to touch each item and make a change.
The easiest way to do this is via linq:
var originallist = new List<string> { "50mph", "35mph", "100mph" };
var newlist = list.Select(s => s.Substring(0, s.Length - 3));
You can use LINQ instead of a foreach loop:
list.Select( s => s.Substring(0, s.Length - 3) )
you can use LINQ for that purpose. Something like this might works :
var noMph = theList.Select(p => p.Replace("mph", "").ToList();
Simple Answer : You Can't
One way or another you have to perform a iteration.
it may be :
foreach
for
List.ForEach
Well you can write
mylist.Select(s=>s.Substring(0, s.Length-3));//Can add .ToList() here
But that is using a loop. You don't have to write the foreach at least :)
this will work
List<string> newList = new List<string>();
mylist.ForEach((item)=>
{
item=item.Replace("mph","");
newlist.Add(item);
});

Duplicate IEnumerable, List and Cast

after reading this very interesting thread on duplicate removal, i ended with this =>
public static IEnumerable<T> deDuplicateCollection<T>(IEnumerable<T> input)
{
var hs = new HashSet<T>();
foreach (T t in input)
if (hs.Add(t))
yield return t;
}
by the way, as i'm brand new to C# and coming from Python, i'm a bit lost between casting and this kind of thing... i was able to compile and build with :
foreach (KeyValuePair<long, List<string>> kvp in d)
{
d[kvp.Key] = (List<string>) deDuplicateCollection(kvp.Value);
}
but i must have missed something here... as i get a "System.InvalidCastException" # runtime, maybe could you point interesting things about casting and where i'm wrong? Thank you in advance.
First, about the usage of the method.
Drop the cast, invoke ToList() on the result of the method. The result of the method is IEnumerable<string>, this is not a List<string>. The fact the source is originally a List<string> is irrelevant, you don't return the list, you yield return a sequence.
d[kvp.Key] = deDuplicateCollection(kvp.Value).ToList();
Second, your deDuplicateCollection method is redundant, Distinct() already exists in the library and performs the same function.
d[kvp.Key] = kvp.Value.Distinct().ToList();
Just be sure you have a using System.Linq; in the directives so you can use these Distinct() and ToList() extension methods.
Finally, you'll notice making this change alone, you run into a new exception when trying to change the dictionary in the loop. You cannot update the collection in a foreach. The simplest way to do what you want is to omit the explicit loop entirely. Consider
d = d.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Distinct().ToList());
This uses another Linq extension method, ToDictionary(). Note: this creates a new dictionary in memory and updates d to reference it. If you need to preserve the original dictionary as referenced by d, then you would need to approach this another way. A simple option here is to build a dictionary to shadow d, and then update d with it.
var shadow = new Dictionary<string, string>();
foreach (var kvp in d)
{
shadow[kvp.Key] = kvp.Value.Distinct().ToList();
}
foreach (var kvp in shadow)
{
d[kvp.Key] = kvp.Value;
}
These two loops are safe, but you see you need to loop twice to avoid the problem of updating the original collection while enumerating over it while also preserving the original collection in memory.
d[kvp.Key] = kvp.Value.Distinct().ToList();
There is already a Distinct extension method to remove duplicates!

c# string.replace in foreach loop

Somehow I can't seem to get string replacement within a foreach loop in C# to work. My code is as follows :
foreach (string s in names)
{
s.Replace("pdf", "txt");
}
Am still quite new to LINQ so pardon me if this sounds amateurish ;)
You say you're after a LINQ solution... that's easy:
var replacedNames = names.Select(x => x.Replace("pdf", "txt"));
We don't know the type of names, but if you want to assign back to it you could potentially use ToArray or ToList:
// If names is a List<T>
names = names.Select(x => x.Replace("pdf", "txt")).ToList();
// If names is an array
names = names.Select(x => x.Replace("pdf", "txt")).ToArray();
You should be aware that the code that you've posted isn't using LINQ at all at the moment though...
Strings in C# are immutable (does not change), so s.Replace will return a new string. Unfortunately this means you cannot use foreach to do the update. If names is an array this should work:
for(int i = 0; i < names.Length; i++)
{
names[i] = names[i].Replace("pdf", "txt");
}
As others have mentioned you'd need to use a for loop to do this in-place. However, if you don't need the operation to be done in-place (i.e. the results can be a different collection), then you could also do it as a linq query, e.g.
var results = from name in names select name.Replace("pdf", "txt");
One thing though - it looks like you are trying to change the extension of some file names. If that's what you are trying to do then I'd recommend Path.ChangeExtension which is specifically designed for this purpose.
var results = from name in names select Path.ChangeExtension(name, "txt");
s.Replace is a function so you would like s=s.Replace().. although it's better to use StringBuilder. (see upper answer)
Why use replace? It will make the application slow. Use regex instead:
http://msdn.microsoft.com/en-us/library/system.text.regularexpressions.regex.replace.aspx

Categories