In my foreach loop, char 'c' characterizes each character in the string currentDependency, and if c is '|', then it adds the position of this to List sectionsSpots. However, it seems to loop through characters that have been already found, which means I probably don't understand exactly how the loop method is working.
In Debug 1, it goes through each of the characters in currentDependency just as expected. In Debug 2, however, when the if statement passes, it always returns an index of 1, which is correct for the first |, as although the first | has an index of 1, the subsequent |'s should have indexes of 19 and 38. Why does the .IndexOf(c) return to the first c that passed in the if statement, while the code should actually be measuring characters later in the string? Thanks in advance!
string currentDependency = ">|Policies/Tax/-0.3|Policies/Trade/0.3|Power/Trader:Farmer/0.4";
List<int> sectionSpots = new List<int> { };
foreach (char c in currentDependency)//add spots of separations of sections
{
Debug.Log("CurrentChar: " + c.ToString());//DEBUG 1
if (c.ToString().Contains("|"))
{
sectionsSpots.Add(currentDependency.IndexOf(c));
Debug.Log("| found in string, index of " + currentDependency.IndexOf(c));//DEBUG 2
}
}
//Output:
//CurrentChar: >
//CurrentChar: |
//| found in string, index of 1
//CurrentChar: P
//CurrentChar: o
//[...]
//CurrentChar: 3
//CurrentChar: |
//| found in string, index of 1////Why is the "index of 1", rather than of 19, if I already made it through the previous | with an index of 1?
//[and so on...]
Each time you are finding the first occurrence (index 2). see: :https://learn.microsoft.com/en-us/dotnet/api/system.string.indexof?view=netframework-4.8;
The easier way how to achieve your goal is:
for (int i = 0; i < currentDependency.Length; i++)
{
if (currentDependency[i] == '|')
{
sectionsSpots.Add(i);
Debug.Log("| found in string, index of " + i);//DEBUG 2
}
}
Using your own code as a base, I modified how you call the IndexOf function to make use of its other parameters.
List<Int32> sectionsSpots = new List<Int32>();
string currentDependency = ">|Policies/Tax/-0.3|Policies/Trade/0.3|Power/Trader:Farmer/0.4";
Int32 startPosition = 0;
foreach (char c in currentDependency)//add spots of separations of sections
{
Debug.Print("CurrentChar: " + c.ToString());//DEBUG 1
if (c.Equals('|'))
{
Int32 position = currentDependency.IndexOf(c, startPosition);
sectionsSpots.Add(position);
Debug.Print("| found in string, index of " + position);//DEBUG 2
startPosition = position + 1;
}
}
By passing in a position, the IndexOf function will start looking for the required character from a different starting point, not the very start of the string.
Related
I found this piece of code on stackoverflow some time ago, but can't seem to find it again. All credit to the author. Sorry I could not link.
QUESTION? Can some C# LINQ guru please break down this statement step by step as I am having difficulty understanding it. It certainly works well and does the job, but how?
Line to Split
var line = $"13351.750815 26646.150876 6208.767863 26646.150876 1219.200000 914.400000 0.000000 1 \"Beam 1\" 0 1 1 1 0 1 1e8f59dd-142d-4a4d-81ff-f60f93f674b3";
var splitLineResult = line.Trim().Split('"')
.Select((element, index) => index % 2 == 0
? element.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
: new string[] { element })
.SelectMany(element => element).ToList();
Result of Statement in LinqPad
You need to begin by analysing the input you have in hand.
13351.750815 26646.150876 6208.767863 26646.150876 1219.200000 914.400000 0.000000 1 "Beam 1" 0 1 1 1 0 1 1e8f59dd-142d-4a4d-81ff-f60f93f674b3
The input consists of few alphanumeric strings separated by Whitespace. However, there is one special case that needs to be handled as well. The word "Beam 1" is enclosed in Quotes.
Now, let's break down the Linq statement.
line.Trim().Split('"')
The first statement splits the input based on the delimiter Quotes. This splits the string into 3 parts.
As you can observe the first(in 0th Index) and Last(in index position 2) needs to be split further while, the element in the in index position 1 has already been parsed. This is where the second part of Linq statement comes into picture.
.Select((element, index) => index % 2 == 0
? element.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
: new string[] { element })
In the above statement the Select((element, index) => index % 2 == 0 part checks if the current index position is in an even position. If so, it needs to be split the substring further based on delimiter ' ' (whitespace). Otherwise, it creates an array with single entity 'Beam 1'
At the end of the second part, what you get is a collection 3 sub-collections of alphanumeric strings (IEnumerble<string[]>).
What now needs to be done is create a collection by flattening the parent collection. This done using Enumerable.SelectMany.
.SelectMany(element => element).ToList();
Hope that helped in understanding the Linq query better
Breaking up the statement
var splitLineResult = line.Trim().Split('"')
.Select((element, index) => index % 2 == 0
? element.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
: new string[] { element }).ToList()
.SelectMany(element => element).ToList();
Trim(): Removes any spaces in the beginning or the end.
Split('"'): Split the string into an array of string with double quote as a delimiter
Select(): Only select a specific part from the element being iterated. In this scenario, its the element and its index in the entire string (starts with 0).
Within your select statement, you have %2 ==0 .. which is true only for every other element. (skips 1)
You can do an if else statement with ? and :
a. If true print 1 else print 0 can be thought of as true ? print 1 : print 0
SelectMany(): Selects all the elements that are returned. Instead of returning 3 arrays, it returns the elements from each of the array.
ToList(): Converts the array to a list.
Seems like there are some really good answers here already, but I had already almost finished mine when I saw people had beat me to it, so here's my version. Hope it brings something to the table.
My approach was writing it out in non Linq code with comments
var line = $"13351.750815 26646.150876 6208.767863 26646.150876 1219.200000 914.400000 0.000000 1 \"Beam 1\" 0 1 1 1 0 1 1e8f59dd-142d-4a4d-81ff-f60f93f674b3";
line = line.Trim(); //remove leading and trailing white space
var tempArray1 = line.Split('"'); //split the line to a string array on "
//so the string array we get, is everything before, between, and after "
//.Select((element, index)
//Basically doing a for-loop on the array we got
List<string[]> tempListForElements = new List<string[]>(); //initialize a temporary list of string arrays we're going to be using
for (var index = 0; index < tempArray1.Length; index++)
{
var element = tempArray1[index];
//now we're getting to the ternary, which is basically like an inline if-else statement
//index % 2 == 0 ? <if true> : <if false>
//if remainder of division by 2 is 0, so basically a way of doing two
//different things for every other iterator of the loop
if (index % 2 == 0)
{
//if on the first or last iteraton on the loop (before and after " in the line)
tempListForElements.Add(element.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries)); //we create yet another string array splitting on each whitespace and add it to our temporary list
}
else
{
//if on the second iteraton on the loop (between ")
tempListForElements.Add(new string[] { element }); //we're creating yet another string array, this time there's just one element in the array though, and then add it to our temporary list
}
}
//.SelectMany(element => element).ToList()
//we're basically turning out list of 3 string array into one string array
//can't be asked to type it out in non linq since just realized there are some good answers here already,
//but imagine initializing a string array with the correct size and then a foreach loop adding each string to it in order.
if you convert this Linq in normal for loop it will look like this.
var line = $"13351.750815 26646.150876 6208.767863 26646.150876 1219.200000 914.400000 0.000000 1 \"Beam 1\" 0 1 1 1 0 1 1e8f59dd-142d-4a4d-81ff-f60f93f674b3";
string[] splitLineResult = line.Trim().Split('"');
var list = new List<string[]>();
for (int i = 0; i < splitLineResult.Length; i++)
{
if (i % 2 == 0)
{
list.Add(splitLineResult[i].Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries));
}
else
{
list.Add(new string[] { splitLineResult[i] });
}
}
var finalList = list.SelectMany(x=>x).ToList();
and for SelectMany method, you can refer MS documentation https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.selectmany?view=netframework-4.8
So picture you having a string like this
o7o7o7o7o7o
There is a clear pattern of o7o
my approach was to find the second o after the first one and that would be the pattern, and then see if it matches through out.
the string is, how do I get the index of the second o ?
I tried this
var pattern = "o7o7o7o7o7o";
var index = input.IndexOf("*");
But that is obviously going to get the first index of the first o it finds, I want to get the second one.
How do I do that?
You can do this many ways, the fastest way would be a loop:
string pattern = "o7o7o7o7o7o";
int count = 0;
int index = 0;
while(index < pattern.Length)
{
if(pattern[index] == 'o') count++;
if(count == 2) break;
index++;
}
and index is what you want.
Linq:
int index = pattern.Select((x, i) => new { x, i })
.Where(a => a.x == 'o').Skip(1)
.FirstOrDefault().i;
string.IndexOf():
int count = 0, index = 0;
do
{
index = pattern.IndexOf('o', index);
if (index != -1) { count++; index++; }
} while (index != -1 && count < 2);
and there are lots of other ways, but I think the three above examples would be fine as other ways I think of are slower (at leat those I can think of).
Also could use Regex like so:
var pattern = "o7o7o7o7o7o";
var regex = new Regex("7(o)");
var matches = regex.Matches(pattern);
foreach (Match match in matches)
{
Console.WriteLine(match.Groups[1].Index);
}
Build prefix function and look for compressed representation as described here
Given a string s of length n. We want to find the shortest
"compressed" representation of the string, i.e. we want to find a
string t of smallest length such that s can be represented as a
concatenation of one or more copies of t.
It is clear, that we only need to find the length of t. Knowing the
length, the answer to the problem will be the prefix of s with this
length.
Let us compute the prefix function for s. Using the last value of it
we define the value k=n−π[n−1]. We will show, that if k divides n,
then k will be the answer, otherwise there doesn't exists an effective
compression and the answer is n.
But your string is not representable as (u)^w because it has excessive char at the end. In this case check divisibility of (i+1)
(i is index) by (i-p[i])
For s = '1231231231' we can get representation (123)^3+1 because the last (i+1) divisible by k[i]=3 is 9
i+1: p[i] k[i]
1 : 0 1
2 : 0 2
3 : 0 3
4 : 1 3
5 : 2 3
6 : 3 3
7 : 4 3
8 : 5 3
9 : 6 3
10 : 7 3
To get the index of the second occurrence of o when there should be at least 1 time not an o in between you might use a regex using a capturing group and get the index of that group:
^[^o]*o[^o]+(o)
That would match:
^ Assert the start of the string
[^o]* Match 0+ times not an o using a negated character class
o Match o literally
[^o]+ Match 1+ times not an o using a negated character class (use [^o]* if there can also be 2 consecutive o's).
(o) Capture o in a group
Regex demo
string pattern = #"^[^o]*o[^o]+(o)";
string input = #"o7o7o7o7o7o";
Match m = Regex.Match(input, pattern);
Console.WriteLine(m.Groups[1].Index); // 2
Demo c#
I have a list of items, each with numbers, followed by a space, and then a word. Think scrabble.
36 adore
36 adore
27 amigo
31 amino
28 amiss
I am trying to use the 2 digit number as an organizational item to which I can rank the words by order of value.
My list, ComJoined is shown above.
My Code is:
for (int i = 0; i < ComJoined.Count; i++)
{
if (i + 1 <= ComJoined.Count)
{
int one = (Convert.ToInt32(ComJoined[i].Substring(0, 2)));
int two = Convert.ToInt32(ComJoined[i + 1].Substring(0, 2));
if (one <= two)
{
string Stuff = ComJoined[i];
ComJoined.Insert(i + 1, Stuff);
ComJoined.RemoveAt(i);
}
}
}
For some reason it says that "Input string was not in a correct format." I read that this meant the string didn't have a int value, but the part being converted, the first two digits, obviously do. Why is this occurring?
So you want to sort the list descending, according to the 2 digit code, and all items start with the two digit code?
This would just be a linq one-liner: var sortedList = ComJoined.OrderByDescending().ToList()
This might be less complex of a solution to your problem:
var words = new SortedDictionary<int, string>();
foreach (var com in ComJoined)
{
string[] splitCom = com.Split(' ');
// Assuming your data is in the correct format. You could use TryParse to avoid an exception.
words.Add(int.Parse(splitCom[0]), splitCom[1]);
}
// Do something with the sorted dictionary...
foreach (KeyValuePair<int, string> word in words)
Console.WriteLine("{0} {1}", word.Key, word.Value);
I am attempting to loop through every combination of an array in C# dependent on size, but not order. For example: var states = ["NJ", "AK", "NY"];
Some Combinations might be:
states = [];
states = ["NJ"];
states = ["NJ","NY"];
states = ["NY"];
states = ["NJ", "NY", "AK"];
and so on...
It is also true in my case that states = ["NJ","NY"] and states = ["NY","NJ"] are the same thing, as order does not matter.
Does anyone have any idea on the most efficient way to do this?
The combination of the following two methods should do what you want. The idea is that if the number of items is n then the number of subsets is 2^n. And if you iterate from 0 to 2^n - 1 and look at the numbers in binary you'll have one digit for each item and if the digit is 1 then you include the item, and if it is 0 you don't. I'm using BigInteger here as int would only work for a collection of less than 32 items and long would only work for less than 64.
public static IEnumerable<IEnumerable<T>> PowerSets<T>(this IList<T> set)
{
var totalSets = BigInteger.Pow(2, set.Count);
for (BigInteger i = 0; i < totalSets; i++)
{
yield return set.SubSet(i);
}
}
public static IEnumerable<T> SubSet<T>(this IList<T> set, BigInteger n)
{
for (int i = 0; i < set.Count && n > 0; i++)
{
if ((n & 1) == 1)
{
yield return set[i];
}
n = n >> 1;
}
}
With that the following code
var states = new[] { "NJ", "AK", "NY" };
foreach (var subset in states.PowerSets())
{
Console.WriteLine("[" + string.Join(",", subset.Select(s => "'" + s + "'")) + "]");
}
Will give you this output.
[]
['NJ']
['AK']
['NJ','AK']
['NY']
['NJ','NY']
['AK','NY']
['NJ','AK','NY']
You can use back-tracking where in each iteration you'll either (1) take the item in index i or (2) do not take the item in index i.
Pseudo code for this problem:
Main code:
Define a bool array (e.g. named picked) of the length of states
Call the backtracking method with index 0 (the first item)
Backtracking function:
Receives the states array, its length, the current index and the bool array
Halt condition - if the current index is equal to the length then you just need to iterate over the bool array and for each item which is true print the matching string from states
Actual backtracking:
Set picked[i] to true and call the backtracking function with the next index
Set picked[i] to false and call the backtracking function with the next index
I'm currently making a game but I seem to have problems reading values from a text file. For some reason, when I read the value, it gives me the ASCII code of the value rather than the actual value itself when I wrote it to the file. I've tried about every ASCII conversion function and string conversion function, but I just can't seem to figure it out.
I use a 2D array of integers. I use a nested for loop to write each element into the file. I've looked at the file and the values are correct, but I don't understand why it's returning the ASCII code. Here's the code I'm using to write and read to file:
Writing to file:
for (int i = 0; i < level.MaxRows(); i++)
{
for (int j = 0; j < level.MaxCols(); j++)
{
fileWrite.Write(level.GetValueAtIndex(i, j) + " ");
//Console.WriteLine(level.GetValueAtIndex(i, j));
}
//add new line
fileWrite.WriteLine();
}
And here's the code where I read the values from the file:
string str = "";
int iter = 0; //used to iterate in each column of array
for (int i = 0; i < level.MaxRows(); i++)
{
iter = 0;
//TODO: For some reason, the file is returning ASCII code, convert to int
//keep reading characters until a space is reached.
str = fileRead.ReadLine();
//take the above string and extract the values from it.
//Place each value in the level.
foreach (char id in str)
{
if (id != ' ')
{
//convert id to an int
num = (int)id;
level.ChangeTile(i, iter, num);
iter++;
}
}
This is the latest version of the loop that I use to read the values. Reading other values is fine; it's just when I get to the array, things go wrong. I guess my question is, why did the conversion to ASCII happen? If I can figure that out, then I might be able to solve the issue. I'm using XNA 4 to make my game.
This is where the convertion to ascii is happening:
fileWrite.Write(level.GetValueAtIndex(i, j) + " ");
The + operator implicitly converts the integer returned by GetValueAtIndex into a string, because you are adding it to a string (really, what did you expect to happen?)
Furthermore, the ReadLine method returns a String, so I am not sure why you'd expect a numeric value to magically come back here. If you want to write binary data, look into BinaryWriter
This is where you are converting the characters to character codes:
num = (int)id;
The id variable is a char, and casting that to int gives you the character code, not the numeric value.
Also, this converts a single character, not a whole number. If you for example have "12 34 56 " in your text file, it will get the codes for 1, 2, 3, 4, 5 and 6, not 12, 34 and 56.
You would want to split the line on spaces, and parse each substring:
foreach (string id in str.Split(' ')) {
if (id.Length > 0) {
num = Int32.Parse(id);
level.ChangeTile(i, iter, num);
iter++;
}
}
Update: I've kept the old code (below) with the assumption that one record was on each line, but I've also added a different way of doing it that should work with multiple integers on a line, separated by a space.
Multiple records on one line
str = fileRead.ReadLine();
string[] values = str.Split(new Char[] {' '});
foreach (string value in values)
{
int testNum;
if (Int32.TryParse(str, out testnum))
{
// again, not sure how you're using iter here
level.ChangeTile(i, iter, num);
}
}
One record per line
str = fileRead.ReadLine();
int testNum;
if (Int32.TryParse(str, out testnum))
{
// however, I'm not sure how you're using iter here; if it's related to
// parsing the string, you'll probably need to do something else
level.ChangeTile(i, iter, num);
}
Please note that the above should work if you write out each integer line-by-line (i.e. how you were doing it via the WriteLine which you remarked out in your code above). If you switch back to using a WriteLine, this should work.
You have:
foreach (char id in str)
{
//convert id to an int
num = (int)id;
A char is an ASCII code (or can be considered as such; technically it is a unicode code-point, but that is broadly comparable assuming you are writing ANSI or low-value UTF-8).
What you want is:
num = (int)(id - '0');
This:
fileWrite.Write(level.GetValueAtIndex(i, j) + " ");
converts the int returned from level.GetValueAtIndex(i, j) into a string. Assuming the function returns the value 5 for a particular i and j then you write "5 " into the file.
When you then read it is being read as a string which consists of chars and you get the ASCII code of 5 when you cast it simply to an int. What you need is:
foreach (char id in str)
{
if (id != ' ')
{
//convert id to an int
num = (int)(id - '0'); // subtract the ASCII value for 0 from your current id
level.ChangeTile(i, iter, num);
iter++;
}
}
However this only works if you only ever are going to have single digit integers (only 0 - 9). This might be better:
foreach (var cell in fileRead.ReadLine().Split(' '))
{
num = Int.Parse(cell);
level.ChangeTile(i, iter, num);
iter++;
}