i've code for indexes string. What should I add to the code so that the results of the string become.
if string "hello" to "loleh"
this the code :
public void IndexesString()
{
string karakter = "hello";
int [] IP = {4,5,3,2,1};
string Result;
txtResult.Text = Result; // Result = "loleh"
}
You can use foreach loop and StringBuilder (especially for long strings)
var stringBuilder = new StringBuilder();
foreach(var i in IP)
{
stringBuilder.Append(karakter[i-1]);
}
Result = stringBuilder.ToString();
string str = "hello";
string result = new string(new[] { str[3], str[4], str[2], str[1], str[0], });
More generally, substitute the indices 3, 4 etc. with ip[n] or similar. Can be done in loop of course.
Or what about this:
string str = "hello";
int[] ip = { 3, 4, 2, 1, 0, };
var result = new string(Array.ConvertAll(ip, idx => str[idx]));
The old ConvertAll method of .NET 2 is similar to Linq's Select method, but with arrays.
Possible Linq solution
string karakter = "hello";
int[] IP = { 4, 5, 3, 2, 1 };
String result = new String(IP.Select(i => karakter[i - 1]).ToArray());
Related
I have the following code:
var strings = new List<string>() { "012abc", "120ccc", "000aaa" };
var str = strings.Select(x => x.ToCharArray());
I need to get the max string on each position returned as a string.
Position 1: 0, 1 and 0 = 1
Position 2: 1, 2 and 0 = 2
Resulting string should be: 122ccc.
Is there a Lambda / Linq query that can be used for this or do I just need to loop through each character?
I supposed that length of all strings is equal
Solution 1: without using Linq.
The idea is: Get max char at the position i in the loop
var strings = new List<string>() { "012abc", "120ccc", "000aaa" };
var arrays = strings.Select(x => x.ToCharArray());
var charResult = new List<char>();
for(int i = 0; i < strings.First().Length;i++)
{
charResult.Add(arrays.Select(x=> x[i]).Max());
}
Output :
122ccc
Solution 2: with Linq
The fiddle works: https://dotnetfiddle.net/MPA8RA
The idea: use Aggregate to compare each pair of char[].
var strings = new List<string>() { "012abc", "120ccc", "000aaa" };
var arrays = strings.Select(x => x.ToCharArray());
var charResult = arrays.Aggregate((a, b) => b.Select((t,i) => (char)Math.Max(t, a[i])).ToArray());
Console.Write(new string(charResult.ToArray()));
Output :
122ccc
I would like to partition collection on item, which matches specific condition. I can do that using TakeWhile and SkipWhile, which is pretty easy to understand:
public static bool IsNotSeparator(int value) => value != 3;
var collection = new [] { 1, 2, 3, 4, 5 };
var part1 = collection.TakeWhile(IsNotSeparator);
var part2 = collection.SkipWhile(IsNotSeparator);
But this would iterate from start of collection twice and if IsNotSeparator takes long it might be performance issue.
Faster way would be to use something like:
var part1 = new List<int>();
var index = 0;
for (var max = collection.Length; index < max; ++index) {
if (IsNotSeparator(collection[i]))
part1.Add(collection[i]);
else
break;
}
var part2 = collection.Skip(index);
But that's really less more readable than first example.
So my question is: what would be the best solution to partition collection on specific element?
What I though of combining those two above is:
var collection = new [] { 1, 2, 3, 4, 5 };
var part1 = collection.TakeWhile(IsNotSeparator).ToList();
var part2 = collection.Skip(part1.Count);
This is a quick example of how you would do the more general method (multiple splits, as mentioned in the comments), without LINQ (it's possible to convert it to LINQ, but I am not sure if it will be any more readable, and I am in a slight hurry right now):
public static IEnumerable<IEnumerable<T>> Split<T>(this IList<T> list, Predicate<T> match)
{
if (list.Count == 0)
yield break;
var chunkStart = 0;
for (int i = 1; i < list.Count; i++)
{
if (match(list[i]))
{
yield return new ListSegment<T>(list, chunkStart, i - 1);
chunkStart = i;
}
}
yield return new ListSegment<T>(list, chunkStart, list.Count - 1);
}
The code presumes a class named ListSegment<T> : IEnumerable<T> which simply iterates from from to to over the original list (no copying, similar to how ArraySegment<T> works (but is unfortunately limited to arrays).
So the code will return as many chunks as there are matches, i.e. this code:
var collection = new[] { "A", "B", "-", "C", "D", "-", "E" };
foreach (var chunk in collection.Split(i => i == "-"))
Console.WriteLine(string.Join(", ", chunk));
would print:
A, B
-, C, D
-, E
How about using the Array Copy methods:
var separator = 3;
var collection = new [] { 1, 2, 3, 4, 5 };
var i = Array.IndexOf(collection,separator);
int[] part1 = new int[i];
int[] part2 = new int[collection.Length - i];
Array.Copy(collection, 0, part1, 0, i );
Array.Copy(collection, i, part2, 0, collection.Length - i );
Alternatively to be more efficient use ArraySegment:
var i = Array.IndexOf(collection,separator);
var part1 = new ArraySegment<int>( collection, 0, i );
var part2 = new ArraySegment<int>( collection, i, collection.Length - i );
ArraySegment is a wrapper around an array that delimits a range of elements in that array. Multiple ArraySegment instances can refer to the same original array and can overlap.
Edit - add combination of original question with ArraySegment so as not to iterate collection twice.
public static bool IsNotSeparator(int value) => value != 3;
var collection = new [] { 1, 2, 3, 4, 5 };
var index = collection.TakeWhile(IsNotSeparator).Count();
var part1 = new ArraySegment<int>( collection, 0, index );
var part2 = new ArraySegment<int>( collection, index, collection.Length - index );
I want to make a string array with values of names and some numbers(which are strings)
i want to pass them into a function that will take the array and then split them into an object jagged array (1 array of strings and 1 array of ints)
the array is:
string[] str= { "toto", "the", "moto", "my", "friend","12","13","14","99","88"};
and the function looks like this:
public object[][] bloop (string[] bstr)
{
}
whats next?
Your scenario looks like bad design that can cause errors and performance issues. The better way is to change code for using generic List<> or something like that. But in your current problem you can use below code:
public object[][] bloop (string[] bstr)
{
var numbers = new List<int>();
var strings = new List<string>();
var result = new object[2][];
foreach(var str in bstr)
{
int number = 0;
if(int.TryParse(str, out number))
{
numbers.Add(number);
}
else
{
strings.Add(str);
}
}
result[0] = strings.ToArray();
result[1] = numbers.ToArray();
return result;
}
public static object[][] bloop(string[] bstr)
{
object[][] result = new object[2][] { new object[bstr.Length], new object[bstr.Length] };
int sFlag = 0, iFlag = 0, val;
foreach (string str in bstr)
if (int.TryParse(str, out val))
result[1][iFlag++] = val;
else
result[0][sFlag++] = str;
return result;
}
I agree that your requirement sounds odd and should be solved with a different approach. However, this will do what you want:
public T[][] Bloop<T>(T[] items)
{
if (items == null) throw new ArgumentNullException("items");
if (items.Length == 1) return new T[][] { items, new T[] { } };
int firstLength = (int) Math.Ceiling((double)items.Length / 2);
T[] firstPart = new T[firstLength];
Array.Copy(items, 0, firstPart, 0, firstLength);
int secondLength = (int)Math.Floor((double)items.Length / 2);
T[] secondPart = new T[secondLength];
Array.Copy(items, firstLength, secondPart, 0, secondLength);
return new T[][] { firstPart, secondPart };
}
Your sample:
string[] str= { "toto", "the", "moto", "my", "friend","12","13","14","99","88"};
string[][] result = Bloop(str);
If you need the second array as int[] you could use following:
int[] ints = Array.ConvertAll(result[1], int.Parse);
Linq solution.
You have two groups: first one has items that can be parsed to int and the second group contains all the others, so GroupBy looks quite naturally:
public Object[][] bloop(string[] bstr) {
if (null == bstr)
throw new ArgumentNullException("bstr");
int v;
return bstr
.GroupBy(x => int.TryParse(x, out v))
.OrderBy(chunk => chunk.Key) // let strings be the first
.Select(chunk => chunk.ToArray())
.ToArray();
}
Test:
string[] str = { "toto", "the", "moto", "my", "friend", "12", "13", "14", "99", "88" };
// toto, the, moto, my, friend
// 12, 13, 14, 99, 88
Console.Write(String.Join(Environment.NewLine,
bloop(str).Select(x => String.Join(", ", x))));
I have a string that might look something like this: "3, 7, 12-14, 1, 5-6"
What i need to do is to change that into a string looking like this: "1, 3, 5, 6, 7, 12, 13, 14"
I have made the following code work, but i would very much appreciated help how to do this a cleaner way with less lines of code:
private string sortLanes(string lanesString)
{
List<string> sortedLanes = new List<string>();
if (lanesString.Contains(',') || lanesString.Contains('-'))
{
List<string> laneParts = lanesString.Split(',').ToList();
foreach (string lanePart in laneParts)
{
if (lanePart.Contains('-'))
{
int splitIndex = lanePart.IndexOf('-');
int lanePartLength = lanePart.Length;
int firstLane = Convert.ToInt32(lanePart.Substring(0, splitIndex));
int lastLane = Convert.ToInt32(lanePart.Substring(splitIndex + 1, lanePartLength - splitIndex - 1));
while (firstLane != lastLane)
{
sortedLanes.Add(firstLane.ToString().Trim());
firstLane++;
}
sortedLanes.Add(lastLane.ToString());
}
else
{
sortedLanes.Add(lanePart.Trim());
}
}
sortedLanes.Sort();
sortedLanes = sortedLanes.OrderBy(x => x.Length).ToList();
lanesString = "";
foreach (string lane in sortedLanes)
{
if (lanesString.Length == 0)
{
lanesString = lane;
}
else
{
lanesString = lanesString + ", " + lane;
}
}
}
else
{
return lanesString;
}
return lanesString;
}
I would first split by the , then convert each value into either a single integer or the desired range. Take the results and reorder them and then concatenate back into a string. Something like this.
string test = "3, 7, 12-14, 1, 5-6";
var items = test.Split(',');
var ints = items.SelectMany(item => Expand(item));
string result = string.Join(", ", ints.OrderBy(i => i).ToArray());
private static IEnumerable<int> Expand(string str)
{
if (str.Contains('-'))
{
var range = str.Split('-');
int begin = int.Parse(range[0]);
int end = int.Parse(range[1]);
for (int i = begin; i <= end; i++)
yield return i;
}
else
yield return int.Parse(str);
}
Of course you might want to add some error checking, but I'll leave that up to you.
This will produce the wanted result (partially based on the incorrect answer from #Tigran):
var parts = "3, 7, 12-14, 1, 5-6".Split(new string[] {", "}, StringSplitOptions.None).ToList();
var finalResult = new List<int>();
foreach(var item in parts)
{
if(item.Contains("-"))
{
var rangeParts = item.Split('-');
var first = int.Parse(rangeParts[0]);
var second = int.Parse(rangeParts[1]);
var result = Enumerable.Range(first, second - first + 1);
finalResult.AddRange(result);
}
else
{
finalResult.Add(int.Parse(item));
}
}
var sorted = finalResult.OrderBy(i => i);
var resultString = string.Join(", ", sorted);
Regex would work nicely here...
static void Main()
{
Assert.AreEqual("1, 3, 5, 6, 7, 12, 13, 14", Transform("3, 7, 12-14, 1, 5-6"));
}
private static string Transform(string input)
{
StringBuilder sb = new StringBuilder();
foreach(Match m in new Regex(#"(?<start>\d+)(?:-(?<end>\d+))?(?:,|$)\s*").Matches(input)
.OfType<Match>().OrderBy(m => int.Parse(m.Groups["start"].Value)))
{
int start = int.Parse(m.Groups["start"].Value);
int end = !m.Groups["end"].Success ? start : int.Parse(m.Groups["end"].Value);
foreach (int val in Enumerable.Range(start, end - start + 1))
sb.AppendFormat("{0}, ", val);
}
if (sb.Length > 0)
sb.Length = sb.Length - 2;//remove trailing comma+space;
return sb.ToString();
}
Update
If I wanted to make it confusing I'd just use a single line of code:
return String.Join(", ",new Regex(#"(?<start>\d+)(?:-(?<end>\d+))?(?:,|$)\s*").Matches(input)
.OfType<Match>().OrderBy(m => int.Parse(m.Groups["start"].Value)).SelectMany(m =>
Enumerable.Range(int.Parse(m.Groups["start"].Value), int.Parse(m.Groups["end"].Success
? m.Groups["end"].Value : m.Groups["start"].Value) - int.Parse(m.Groups["start"].Value) + 1))
.Select(i => i.ToString()).ToArray());
LoL :)
If you like Linq:
string input = "3, 7, 12-14, 1, 5-6";
List<int> all = input.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(r => new
{
Range = r,
Parts = r.Split(new[] { '-' }, StringSplitOptions.RemoveEmptyEntries)
.Select(p => int.Parse(p))
})
.SelectMany(x => Enumerable.Range(x.Parts.First(), 1 + x.Parts.Last() - x.Parts.First()))
.ToList();
Demo
having a List<int> of integers (for example: 1 - 3 - 4)
how can I convert it in a string of this type?
For example, the output should be:
string values = "1,3,4";
var nums = new List<int> {1, 2, 3};
var result = string.Join(", ", nums);
var ints = new List<int>{1,3,4};
var stringsArray = ints.Select(i=>i.ToString()).ToArray();
var values = string.Join(",", stringsArray);
Another solution would be the use of Aggregate. This is known to be much slower then the other provided solutions!
var ints = new List<int>{1,2,3,4};
var strings =
ints.Select(i => i.ToString(CultureInfo.InvariantCulture))
.Aggregate((s1, s2) => s1 + ", " + s2);
See comments below why you should not use it. Use String.Join or a StringBuilder instead.
public static string ToCommaString(this List<int> list)
{
if (list.Count <= 0)
return ("");
if (list.Count == 1)
return (list[0].ToString());
System.Text.StringBuilder sb = new System.Text.StringBuilder(list[0].ToString());
for (int x = 1; x < list.Count; x++)
sb.Append("," + list[x].ToString());
return (sb.ToString());
}
public static List<int> CommaStringToIntList(this string _s)
{
string[] ss = _s.Split(',');
List<int> list = new List<int>();
foreach (string s in ss)
list.Add(Int32.Parse(s));
return (list);
}
Usage:
String s = "1,2,3,4";
List<int> list = s.CommaStringToIntList();
list.Add(5);
s = list.ToCommaString();
s += ",6";
list = s.CommaStringToIntList();
You can use the delegates for the same
List<int> intList = new List<int>( new int[] {20,22,1,5,1,55,3,10,30});
string intStringList = string.Join(",", intList.ConvertAll<string>(delegate (int i) { return i.ToString(); });
Use the Stringify.Library nuget package
Example 1 (Default delimiter is implicitly taken as comma)
string values = "1,3,4";
var output = new StringConverter().ConvertFrom<List<int>>(values);
Example 2 (Specifying the delimiter explicitly)
string values = "1 ; 3; 4";
var output = new StringConverter().ConvertFrom<List<int>>(values), new ConverterOptions { Delimiter = ';' });