Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
So I was attempting a challenge on Codewars and after completing it, I saw the following solution.
public static List<string> SinglePermutations(string s)
=> $"{s}".Length < 2
? new List<string> { s }
: SinglePermutations(s.Substring(1))
.SelectMany(x => Enumerable.Range(0, x.Length + 1)
.Select((_, i) => x.Substring(0, i) + s[0] + x.Substring(i)))
.Distinct()
.ToList();
Could anyone explain this to me? I have seen the ternary operator and recursion before. I am relatively new to Select and Select many. However, putting all these into one question has confused me. Thanks!
After simplifications I noted in my comments, here is some explanation:
public static List<string> SinglePermutations(string s)
=> s.Length == 1
? new List<string> { s } // a single character string only has one permuatation, itself
: SinglePermutations(s.Substring(1)) // get all the permutations of the "tail" of the string i.e. the string minus the first char
// for each tail permutation
.SelectMany(aTailPermutation =>
// loop over all the positions in the permutation from before the first char to after the last char
Enumerable.Range(0, aTailPermutation.Length + 1)
// for each position, put the original first char in that position
.Select(i => aTailPermutation.Substring(0, i) + s[0] + aTailPermutation.Substring(i))
) // flatten the resulting list of strings
.Distinct() // throw away any duplicates
.ToList(); // return as a list
It might be easier understood if converted to procedural code from LINQ:
public static List<string> SinglePermutationsFn(string s) {
if (s.Length == 1) // a single character string
return new List<string> { s }; // only has one permuatation, itself
else {
var ans = new List<string>();
// get all the permutations of the "tail" of the string i.e. the string minus the first char
var tailPermutations = SinglePermutations(s.Substring(1));
// loop over all the tail permutations
foreach (var aTailPermutation in tailPermutations) {
// loop over all the positions in the permutation from before the first char to after the last char
for (int permutationPos = 0; permutationPos < aTailPermutation.Length + 1; ++permutationPos) {
// for each position, put the original first char in that position
ans.Add(aTailPermutation.Substring(0, permutationPos) + s[0] + aTailPermutation.Substring(permutationPos));
}
}
return ans.Distinct() // throw away any duplicates
.ToList(); // return as a list
}
}
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed last year.
Improve this question
I am pretty new to C# and I would like to know how more experienced programmers would solve this issue. In this code the is a private const string with a bunch of numbers and letters. I need my program to find which number occurs the most frequently and how many times it occurs and spit out the results.
public class Program
{
private const string FileContents = "\"12345.56789\",\"12345.56789\",\"12345.56789\",\"12345.56789\"";
public static void Main()
{
}
}
You can use GroupBy on each number/word and then check count of element in each group,
private const string FileContents = "\"12345.56789\",\"12345.56789\",\"12345.56789\",\"12345.56789\"";
var mostFrequent = FileContents
.Split(',')
.GroupBy(x => x)
.Select(x => new {Key = x.Key, Count = x.Count()})
.OrderByDescending(x => x.Count)
.FirstOrDefault();
mostFrequent variable will contain either number or word(letters) with highest occurrences in the given const string.
If you want only number not a word, then you have to filter split array into number array, like below
var mostFrequentNumber = FileContents
.Split(',')
.Select(x => x.Trim("\"")) //Remove leading and trailing \"
.Where(x => float.TryParse(x, out _)) //Check string is numeric or not
.GroupBy(x => x)
.Select(x => new {Key = x.Key, Count = x.Count()})
.OrderByDescending(x => x.Count)
.FirstOrDefault();
You can make this following these steps:
Parse the string into the collection of numbers
Group numbers (group should contain the number itself and the count for how many times it occurs)
Order groups by the number of occurrences (in descending order) and take the first one (hence the one with the most number of occurrences)
Extract the number from the group
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I have a list of strings like this:
123.45 ABC
678.90 DEF
543.21 FED
I want to sort the list alphabetically using the last 3 characters of each list element.
EDIT: Using C#, how do I alphabetize this list by using the last 3 characters of each element?
What I would first do is to ensure that I filter out all strings that have less than 3 characters. Then I would order them as:
var items = new List<string> { "13 zzz", "12 yyy", "11 zzz" };
items = items.Where(i => i.Length > 2)
.OrderBy(i => i.Substring(i.Length - 3))
.ToList();
To order by the last three characters, you can just take the Substring starting at Length - 3 and use that in the OrderBy method (note that we should first check that item.Length > 2 so that Substring doesn't throw an execption):
var items = new List<string> {"543.21 FED", "123.45 ABC", "678.90 DEF"};
items = items
.OrderBy(item => item?.Length > 3 ? item.Substring(item.Length - 3) : item)
.ToList();
// result: {"123.45 ABC", "678.90 DEF", "543.21 FED"}
Alternatively, you can write a custom comparer for the strings and then pass that to the Sort method. We can include a constructor for our comparer that takes in an int that specifies how many characters we want to count from the end of the string to make it more flexible (like if you want to use the last 5 characters, or the last 2, etc.).
public class OrderByLastNChars : Comparer<string>
{
public int N { get; set; }
public OrderByLastNChars(int n)
{
N = n;
}
public override int Compare(string x, string y)
{
if (x == null) return y == null ? 0 : -1;
if (y == null) return 1;
var first = x.Length > N ? x.Substring(x.Length - N) : x;
var second = y.Length > N ? y.Substring(x.Length - N) : y;
return first.CompareTo(second);
}
}
Then this can be used like:
items.Sort(new OrderByLastNChars(3));
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
It's a little confusing but if you look at the example below you get it!
I have a special "Console.Write" method that takes a string, for example "§cHello %mThere!" and when printing to the Console, this method ignores the §c and %m (it changes the color of the Console).
Now, I have another method to print text by aligning it, by breaking the string into an array of strings every nth char. That means, if I pass a string of 100 chars and a LineLength of 10, it will break my string into an array of 10 strings with 10 chars each, then my Write method will print each one in a new line.
The problem is, when I split the text every nth char, it count the '§' and 'c' chars, and when I print it (the method for printing will remove those two), so the output is off by two chars.
So... I need a method that:
Splits a string into an array of strings every nth character.
However, it must not count '§' and the following char, or '%' and the next char as characters in that math.
The output must have those extra chars in the string array.
Example:
string Text = "§cOnce upon a time there was a §R%mnice girl named Cinderella. She was very poor and her father passed way."
int LineLength = 6;
return string[] Output = {
"§conce u" //[o n c e space u], thats 6 chars ignoring the first two.
"pon a " //[p o n space a space], thats 6 chars.
"time t" // etc
//etc
}
If someone help me write this, thanks in advance!!!
If I understand what you're saying this seems about right.
public static string[] ConsoleChunk(string input, int length){
List<string> Output = new List<string>();
int k = 0;
string currentString = "";
for(int i = 0; i < input.Length; i++){
if(k == 6){
Output.Add(currentString);
currentString = input[i].ToString();
k = 1;
}
else if(input[i] == '§' || input[i] == '%'){
currentString += input[i];
currentString += input[++i];
}
else{
k++;
currentString += input[i];
}
}
Output.Add(currentString);
return Output.ToArray();
}
Input
string test = "§cOnce upon a time there was a §R%mnice girl named Cinderella. She was very poor and her father passed way.";
Output
§cOnce u
pon a
time t
here w
as a §R%mn
ice gi
rl nam
ed Cin
derell
a. She
was v
ery po
or and
her f
ather
passed
way.
Given
public static IEnumerable<string> FunkyChunk(string source, int size)
{
var index = 0;
while (index < source.Length)
{
var sb = new StringBuilder(size*2);
for (int count = 0; count<size && index < source.Length; index++)
{
sb.Append(source[index]);
if (source[index] != '§' && source[index] != '%')
count++;
}
yield return sb.ToString();
}
}
Note : This is O(n) and used StringBuilder for less allocations even though there would be more succinct solutions. Using a fixed buffer and another index would likely be better
Usage
var input = "012345§678901234567%890123%4567890123456§§789012§345678901234";
foreach (var result in FunkyChunk(input,10))
Console.WriteLine(result);
Output
012345§6789
01234567%89
0123%456789
0123456§§789
012§3456789
01234
Full Demo Here
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
below is the Method I am trying to solve.....
public int IndexOfLastUniqueLetter(string str)
I have not tried any code yet because I have no Idea how to solve it.
Please advise how to solve this method and the code which to do so. Please and thank you.
The algorithm boils down to these two steps:
Find which letters are unique,
Find the last of these unique letters in the original string.
Counting occurrences of each letter could be done with a loop, or with an associative container, depending on your preferences. If count is 1, the letter is unique; otherwise, it is not unique. Finding last-of could be done by walking the string backward.
The only thing to worry about here is what to do when there are no unique letters in the string (e.g. "aaaa" or "abab"). Your code should detect this condition explicitly, and return -1.
If i may suggest a single line of code,
Get a list of all unique letters
Iterate through backwards to see which one occurs only once in the string.
Return the index of that letter (-1 if it doesnt exist)
public static int IndexOfLastUniqueLetter(string original)
{
return original.IndexOf(
original.Distinct()
.Reverse()
.Where(x => original.Where(y => y.Equals(x)).Count() == 1)
.FirstOrDefault());
}
Easiest method is to parse twice the string.
I personally used a Dictionary<char, int> which is storing the occurrence for any character in my string.
Once parsed, and because Dictionary does not preserve order, you can (as dasblinkenlight) mentioned parse your string backward.
Below is my method:
int GetIndexOfLastUniqueOccurrence(string msg)
{
var occurrenceChar = new Dictionary<char, int>();
// Parse the string to count occurrence
for (int i = 0; i < msg.Length; i++)
{
if (occurrenceChar.ContainsKey(msg[i]))
occurrenceChar[msg[i]]++;
else
occurrenceChar.Add(msg[i], 1);
}
// Parse backward the string to find the last one with occurrence == 1
for (int i = msg.Length - 1; i >= 0; i--)
{
var occurrence = occurrenceChar[msg[i]];
if (occurrence == 1)
return i;
}
return -1; // If string does not contain any unique char
}
Below are some tests:
Console.WriteLine(GetIndexOfLastUniqueOccurrence("abcddeeff")); // 2
Console.WriteLine(GetIndexOfLastUniqueOccurrence("aaaaaabbbbbcccc")); // -1
Console.WriteLine(GetIndexOfLastUniqueOccurrence("abcdefgg")); // 5
Something less verbose
If you want to create your dict out of the string using LINQ:
var occurrenceChar = msg.GroupBy(c => c).Select(c => new { Char = c.Key, Count = c.Count() }).ToDictionary(x => x.Char, x => x.Count);
Something even less verbose (inspired by #Jawad anwser):
return message.IndexOf(
message
.Distinct()
.Reverse()
.FirstOrDefault(x => message.Count(y => y.Equals(x)) == 1));
It's easy to count occurrences of words in a file by using a Dictionary to identify which words are used the most frequently, but given a text file, how can I find commonly used phrases where a "phrase" is a set of two or more consecutive words?
For example, here is some sample text:
Except oral wills, every will shall be in writing, but may be
handwritten or typewritten. The will shall contain the testator's signature
or by some other person in the testator's conscious presence
and at the testator's express direction . The will shall be attested
and subscribed in the conscious presence of the testator, by two or
more competent witnesses, who saw the testator subscribe, or heard the
testator acknowledge the testator's signature.
For purposes of this section, conscious presence means within the
range of any of the testator's senses, excluding the sense of sight or
sound that is sensed by telephonic, electronic, or other distant
communication.
How can I identify that the phrases "conscious presence" (3 times) and "testator's signature" (2 times) as having appeared more than once (apart from brute force searching for every set of two or three words)?
I'll be writing this in c#, so c# code would be great, but I can't even identify a good algorithm so I'll settle for any code at all or even pseudo code for how to solve this.
Thought I'd have a quick go at this - not sure if this isn't the brute force method you were trying to avoid - but :
static void Main(string[] args)
{
string txt = #"Except oral wills, every will shall be in writing,
but may be handwritten or typewritten. The will shall contain the testator's
signature or by some other person in the testator's conscious presence and at the
testator's express direction . The will shall be attested and subscribed in the
conscious presence of the testator, by two or more competent witnesses, who saw the
testator subscribe, or heard the testator acknowledge the testator's signature.
For purposes of this section, conscious presence means within the range of any of the
testator's senses, excluding the sense of sight or sound that is sensed by telephonic,
electronic, or other distant communication.";
//split string using common seperators - could add more or use regex.
string[] words = txt.Split(',', '.', ';', ' ', '\n', '\r');
//trim each tring and get rid of any empty ones
words = words.Select(t=>t.Trim()).Where(t=>t.Trim()!=string.Empty).ToArray();
const int MaxPhraseLength = 20;
Dictionary<string, int> Counts = new Dictionary<string,int>();
for (int phraseLen = MaxPhraseLength; phraseLen >= 2; phraseLen--)
{
for (int i = 0; i < words.Length - 1; i++)
{
//get the phrase to match based on phraselen
string[] phrase = GetPhrase(words, i, phraseLen);
string sphrase = string.Join(" ", phrase);
Console.WriteLine("Phrase : {0}", sphrase);
int index = FindPhraseIndex(words, i+phrase.Length, phrase);
if (index > -1)
{
Console.WriteLine("Phrase : {0} found at {1}", sphrase, index);
if(!Counts.ContainsKey(sphrase))
Counts.Add(sphrase, 1);
Counts[sphrase]++;
}
}
}
foreach (var foo in Counts)
{
Console.WriteLine("[{0}] - {1}", foo.Key, foo.Value);
}
Console.ReadKey();
}
static string[] GetPhrase(string[] words, int startpos, int len)
{
return words.Skip(startpos).Take(len).ToArray();
}
static int FindPhraseIndex(string[] words, int startIndex, string[] matchWords)
{
for (int i = startIndex; i < words.Length; i++)
{
int j;
for(j=0; j<matchWords.Length && (i+j)<words.Length; j++)
if(matchWords[j]!=words[i+j])
break;
if (j == matchWords.Length)
return startIndex;
}
return -1;
}
Try this out. It's in no way fool-proof, but should get the job done for now.
Yes, this only matches 2-word combos, does not strip punctuation, and is brute-force. No, the ToList is not necessary.
string text = "that big long text block";
var splitBySpace = text.Split(' ');
var doubleWords = splitBySpace
.Select((x, i) => new { Value = x, Index = i })
.Where(x => x.Index != splitBySpace.Length - 1)
.Select(x => x.Value + " " + splitBySpace.ElementAt(x.Index + 1)).ToList();
var duplicates = doubleWords
.GroupBy(x => x)
.Where(x => x.Count() > 1)
.Select(x => new { x.Key, Count = x.Count() }).ToList();
I got the following results:
Here is my attempt at getting more than 2 word combos. Again, same warning as previous.
List<string> multiWords = new List<string>();
//i is the number of words to combine
//in this case, 2-6 words
for (int i = 2; i <= 6; i++)
{
multiWords.AddRange(splitBySpace
.Select((x, index) => new { Value = x, Index = index })
.Where(x => x.Index != splitBySpace.Length - i + 1)
.Select(x => CombineItems(splitBySpace, x.Index, x.Index + i - 1)));
}
var duplicates = multiWords
.GroupBy(x => x)
.Where(x => x.Count() > 1)
.Select(x => new { x.Key, Count = x.Count() }).ToList();
private string CombineItems(IEnumerable<string> source, int startIndex, int endIndex)
{
return string.Join(" ", source.Where((x, i) => i >= startIndex && i <= endIndex).ToArray());
}
The results this time:
Now I just want to say there is a high chance of a off-by-one error with my code. I did not fully test it, so make sure you test it before you use it.
If I were doing it, I would probably be starting with the brute force approach, but it sounds like you want to avoid that. A two-phase approach could do a count of each word, take the top few results (only start with the top few words that appear the most times), then search for and count only phrases that include these popular words. Then you won't spend your time searching over all phrases.
I have this feeling that CS folks will correct me saying that this would actually take more time than a straight up brute force. And maybe some linguists will pitch in with some methods for detecting phrases or something.
Good luck!