c# string.replace in foreach loop - c#

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

Related

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

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();

using lambda, how to apply an existing function to all elements of a list?

Excuse me, a quick question:
I have a list of strings, string are full paths of some files. I would like to get only the filename without the path neither the extension for each string (and to understand lambda more)
Based on the lambda expression in How to bind a List to a DataGridView control? I am trying something like the below:
FilesName = Directory.GetFiles(fbd.SelectedPath).ToList(); // full path
List<string> FilesNameWithoutPath = AllVideosFileNames.ForEach(x => Path.GetFileNameWithoutExtension(x)); // I want only the filename
AllVideosGrid.DataSource = FilesNameWithoutPath.ConvertAll(x => new { Value = x }); // to then bind it with the grid
The error is:
Can not convert void() to List of string
So I want to apply Path.GetFileNameWithoutExtension() for each string in FilesName. And would appreciate any extra description on how Lamba works in this case.
ForEach will execute some code on each item in your list, but will not return anything (see: List<T>.ForEach Method). What you want to do is Select the result of the method (see: Enumerable.Select<TSource, TResult> Method), which would look something like:
List<string> FilesNameWithoutPath = AllVideosFileNames
.Select(x => Path.GetFileNameWithoutExtension(x))
.ToList();
You are using List<T>.ForEach method which takes each element in the list and applies the given function to them, but it doesn't return anything. So what you are doing basically is getting each file name and throwing them away.
What you need is a Select instead of ForEach:
var fileNamesWithoutPath = AllVideosFileNames
.Select(x => Path.GetFileNameWithoutExtension(x))
.ToList();
AllVideosGrid.DataSource = fileNamesWithoutPath;
This will project each item, apply Path.GetFileNameWithoutExtension to them and return the result, then you put that result into a list by ToList.
Note that you can also shorten the Select using a method group without declaring a lambda variable:
.Select(Path.GetFileNameWithoutExtension)

Using linq to execute an expression

I was studying about linq and wondering can linq be applied and used in the scenario bellow or not;
Suppose we split a string with space as delimiter and want to add every item from the result of split into a list if item is not already in the list;
string text = "This is just a test!";
List<string> uniqueList = new List<string>();
foreach (string item in text.Split(' '))
{
if (!uniqueList.Contains(item))
{
uniqueList.Add(item);
}
}
using linq I can write (as far as I know):
var items = from item in text.Split(' ')
where !uniqueList.ContainsKey(item)
select item;
items now is a collection and I have to iterate it another time to add the items in to the uniqueList.
Is there a capability in linq to combine the second and third computations (removing the need for second iteration) or I can't get better than the first solution?
Please note that this is just an example, consider it broadly, maybe next time I want to show a dialog box for every matched item rather than adding into a list.
You can use :
string text = "This is just a test! is This aa";
var uniqueList = text.Split(' ').Distinct().ToList();
If you use method syntax, you can do your Select using a lambda expression with scope, where you can execute more than one operation:
string text = "This is just a test ! test";
var uniqueList = new List<string>();
var items = text.Split(' ').Where(s => !uniqueList.Contains(s))
.Select(s=> {
uniqueList.Add(s);
return s;
})
.ToList();
Yes, this can accomplished elegantly via Linq (and more efficiently too, because Contains was causing it to be O(n^2) -- the Distinct Linq method exists for exactly this purpose):
var uniqueList = text.Split(' ').Distinct().ToList();
Does it matter what order the elements are in the list? If not, you could use a collection that implements ISet (like HashSet):
ISet<string> uniqueList = new HashSet<string>();
foreach (string item in text.Split(' '))
{
uniqueList.Add(item);
}
This lets the collection decide if it needs to add the item or not (.Add will return true if it did). It just doesn't guarantee enumerating in the same order in which they were added. I use these a lot for "is it there?" kind of tests. Kind of like a dictionary without a value.
uniqueList.AddRange(text.Split(' ').Where(s => !uniqueList.Contains(s)));
Edit (since OP was edited indicating that adding items to a list is not the actual intent)
Linq executes queries and provides result sets. It's not about executing code using the results as parameters.
For what it's worth, if you have your results in a List<T> you can do this:
myList.ForEach(itemInList => {
// Execute multiple statements using each item in the list
});
or
myList.ForEach(itemInList => DoSomethingWithItem(itemInList));
or even shorter,
myList.ForEach(DoSomethingWithItem);
But it's just for convenience. It's really no different from a for...each loop.

Replace string values in list

I have a collection of strings which contain values like "goalXXvalue,goalXXLength,TestXX". It is a List(of String)
I thought I would be able to loop through each item and replace the XX value which I've tried with the method below but the values don't change. Where am I going wrong?
Thanks
metricList.ForEach(Function(n) n.Replace("XX", "1"))
You have a few issues here:
first, strings are immutable, so when you call .Replace you return a new string. Calling n.Replace doesn't modify n.
assigning to n in your anonymous function won't affect the value that's in your list.
regardless of the above, you can't change the content of your collection while enumerating it, because it'll invalidate the enumeration.
Since it seems you're changing every string in your list, it seems unnecessary to try to modify the collection in-place. Therefore, the succint solution would be to use Linq would to create a new list:
var newList = metricList.Select(s => s.Replace("XX", "1")).ToList();
Problem: You aren't doing anything with the Replaced strings.
You could easily do this, using a simple loop:
C#
for(int i = 0; i < metricList.Count; i++)
{
metricList[i] = metricList[i].Replace("XX", "1");
}
VB.NET
For i As Integer = 0 To metricList.Count - 1
metricList(i) = metricList(i).Replace("XX", "1")
Next
Code iterates through all strings in metricList and replaces XX for 1, it then stores the values back at the correct place in the list, what you aren't doing in your code...
Or using Linq:
C#
var newList = metricList.Select(x => x.Replace("XX", "1")).ToList();
VB.NET
Dim newList = metricList.Select(Function(x) x.Replace("XX", "1")).ToList()
Don't forget to add a reference to linq at the top of your class:
C#
using System.Linq;
VB.NET
Imports System.Linq
You need to assign result of String.Replace method. So your func should return something or use instead of foreach select

Is there a way to make it appear like a foreach look returns a value?

This is something I have always wondered about, and looked up a few times but have never figured out.
So basically what I want to do is get something to this effect:
List<string> strings = new List<string>(){"a","b","c"};
string aString = foreach(string s in strings){ if (s == "c") return s;}
so then after that, aString has the value "c".
I have tried using lambda expressions, maybe I just cant get them to work right, or maybe there is just no way to do this.
And obviously I want to do something a bit more complicated than in my example above, but it will work the same way.
Possible? not possible?
You should use the FirstOrDefault Extension method.
List<string> strings = new List<string>(){"a","b","c"};
return strings.FirstOrDefault(s=>String.Equals(s, "a"));
You can use LINQ (to objects):
List<string> strings = new List<string>(){"a","b","c"};
string aString = strings.Where(x => x.Equals("a")).FirstOrDefault();
The Where() methods iterates through the enumerable, and "returns" each element that satisfies the lambda. To get the first such element, you can chain on the FirstOrDefault() method (which will return default(string) if no elements meet the criteria.)
As #MichaelGraczyk points out, you can actually reduce the call to only FirstOrDefault(), since it has an overload that accepts a predicate:
string aString = strings.FirstOrDefault(x => x.Equals("a"));
There are a number of other useful methods available, which you can read about here.
It's kind of pointless in this particular example because you already know the string you want but in any case I think this is what you're trying to do...
List<string> strings = new List<string>(){"a","b","c"};
string aString = strings.Find((string s) => s == "a");

Categories