I want to insert a random amount of dots (from 1 to 7) on random parts of a string without breaking the layout.
This is my current code:
Random rand = new Random();
string[] words = iTemplate.Text.Split(' ');
string result = string.Empty;
for (int i = 0; i < words.Count(); i++)
{
string word = words[i];
if (rand.Next(i, words.Count()) == i)
{
for (int dots = rand.Next(1, 7); dots > 0; dots--)
word += ".";
}
result += word + " ";
}
Is there a more efficient or nice LINQ option to it ?
Right now, since its random, there may be cases of no dots showing up. I have narrowed it by using if (rand.Next(i, words.Count()) == i) which seems to work but still some results only show 1 to 3 places having dots inserted.
How could I guarantee that the dots are inserted a minimum of 4 different places during the process ?
Sample data/result as per comment request:
string template = "Hi, this is a template with several words on it and I want to place random dots on 4 different random places every time I run the function";
Result 1:
string result = "Hi, this... is a template with several.. words on it and. I want to place random dots on 4 different random...... places every time I run the function";
Result 2:
string result = "Hi, this is a template. with several... words on it and I want to..... place random dots on 4 different random. places every time I run the function";
Result 3:
string result = "Hi, this. is a template with... several words on it and I want to place random.. dots on 4 different random....... places every time I run the.. function";
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
class Program
{
static void Main(string[] args)
{
Random rand = new Random();
string[] words = "Now is the time for all good men to come to the aid of their countrymen".Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (words.Length > 0)
{
// Generate a list of integers from 0 to words.Length - 1
List<int> addIndices = Enumerable.Range(0, words.Length).ToList();
// Shuffle those indices
Shuffle(addIndices, rand);
// Pick the number of words that will have dots added
int addCount = rand.Next(4, Math.Max(4, words.Length));
// Truncate the array so that it only contains the first addCount items
addIndices.RemoveRange(addCount, addIndices.Count - addCount);
StringBuilder result = new StringBuilder();
for (int i = 0; i < words.Length; i++)
{
result.Append(words[i]);
if (addIndices.Contains(i)) // If the random indices list contains this index, add dots
result.Append('.', rand.Next(1, 7));
result.Append(' ');
}
Console.WriteLine(result.ToString());
}
}
private static void Shuffle<T>(IList<T> array, Random rand)
{
// Kneuth-shuffle
for (int i = array.Count - 1; i > 0; i--)
{
// Pick random element to swap.
int j = rand.Next(i + 1); // 0 <= j <= i
// Swap.
T tmp = array[j];
array[j] = array[i];
array[i] = tmp;
}
}
}
This will ensure that you add dots to at least 4 words, as well as not adding a trailing space to your final string.
Random rand = new Random();
string[] words = iTemplate.Text.Split(' ');
// Insert dots onto at least 4 words
int numInserts = rand.Next(4, words.Count());
// Used later to store which indexes have already been used
Dictionary<int, bool> usedIndexes = new Dictionary<int, bool>();
for (int i = 0; i < numInserts; i++)
{
int idx = rand.Next(1, words.Count());
// Don't process the same word twice
while (usedIndexes.ContainsKey(idx))
{
idx = rand.Next(1, words.Count());
}
// Mark this index as used
usedIndexes.Add(idx, true);
// Append the dots
words[idx] = words[idx] + new String('.', rand.Next(1, 7));
}
// String.Join will put the separator between each word,
// without the trailing " "
string result = String.Join(" ", words);
Console.WriteLine(result);
This code assumes that you do in fact have at least 4 words in iTemplate.Text. If there's a chance that you won't, you should add some additional validation logic.
Well, if you need at least 4 different places, you need at least four dots. You do it in two parts - first choose the 4 words that get a dot at the end (that is, choose a word at random, add a dot to it, and make sure you don't choose it again), then choose 3 words at random, with repetitions, and add dots to them.
Just for fun and as terse as I can make it:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var random = new Random();
var iTemplate = "Hi, this is a template with several words on it and I want to place random dots on 4 different random places every time I run the function";
var result = iTemplate;
while (new Regex("\\. ").Matches(result).Count < 4)
result = result.TrimEnd()
.Split(' ')
.Aggregate(
string.Empty,
(current, word) =>
current + (word + (((word.EndsWith(".") || (random.Next(1, 100) % 10) != 0)) ? "" : new string('.', random.Next(1, 7))) + " ")
);
Console.WriteLine(result);
Console.Read();
}
}
}
Related
I'm trying to cycle through chars in a string.
string cycleMe = "Hi StackOverflow! Here is my string."
However, I want to skip over certain ranges of indexes. The ranges I want to skip over are stored in a List of objects, delims.
List<Delim> delims = delimCreator();
To retrieve each starting index and ending index for a range, I have to write a loop that accesses each "delim":
delims[0].getFirstIndex() //results in, say, index 2
delims[0].getLastIndex() //results in, say, index 4
delims[1].getFirstIndex() //results in, say, index 5
delims[1].getLastIndex() //results in, say, index 7
(there can be infinitely many "delim" objects in play)
If the above were my list, I'd want to print the string cycleMe, but skip all the chars between 2 and 4 (inclusive) and 5 and 7 (inclusive).
Expected output using the numbers above:
HiOverflow! Here is my string.
Here is the code I have written so far. It loops far more often than I'd expect (it loops ~x2 the number of characters in the string). Thanks in advance! =)
List<Delim> delims = delimAggregateInator(displayTextRaw);
for (int x = 0; x < cycleMe.Length;x++){
for (int i = 0; i < delims.Count; i++){
if (!(x >= delims[i].getFirstIndex() && x <= delims[i].getLastIndex())){
Debug.Log("test");
}
}
I assume that by skipping you meant you want to omit those characters from the original string. If that is the case, you can try Aggregate extension method like below.
string result = delims.Aggregate<Delim, string>(cycleMe, (str, d) => cycleMe = cycleMe.Remove(d.FirstIndex, (d.LastIndex - d.FirstIndex) + 1));
Make sure that the delim list is in the proper order.
Solution might be converting the string to char array, replacing the desired parts to spaces, and converting the output back to string.
Here is the modified version of your code:
string cycleMe = "Hi StackOverflow! Here is my string."
var charArray = cycleMe.ToCharArray(); // Converting to char array
List<Delim> delims = delimAggregateInator(displayTextRaw);
for (int x = 0; x < cycleMe.Length;x++){
for (int i = 0; i < delims.Count; i++){
// ORIGINAL: if (!(x >= delims[i].getFirstIndex() && x <= delims[i].getLastIndex())){
if (x >= delims[i].getFirstIndex() && x <= delims[i].getLastIndex()){
Debug.Log("test");
charArray[x] = ' '; // Replacing the item with space
}
}
string output = new string(charArray); // Converting back to string
P.S. This is probably not the most optimal solution but at least it should work.
You should use LINQ for that
struct Delim
{
public int First { get; set; }
public int Last { get; set; }
}
static void Main(string[] args)
{
string cycleMe = "Hi StackOverflow! Here is my string.";
var delimns = new List<Delim> { new Delim { First=2, Last=4}, new Delim { First = 5, Last = 7 } };
var cut = cycleMe.Where((c, i) =>
!delimns.Any(d => i >= d.First && i <= d.Last));
Console.WriteLine(new string(cut.ToArray());
}
That means I am basically only selecting letters, at positions which are not part of any cutting range.
Also: Fix your naming. A delimiter is a character, not a position (numeric)
I want to build a program that analyses sentences, and then for each character/number/symbol that appears in the words of the sentence, record which words the character appears in. (upper/lower case to be ignored, and duplicate entries of a character in a word are ignored).
So if I had the sentence "I wandered lonely as a cow".
After the first word, i'd have a data construct... i - 1; // because "I" occurred in the first word.
after the second word, my data construct would be... i - 1; w - 2; a - 2; n - 2; d - 2; e - 2; r - 2;
after the sixth word... i - 1; w - 2,6; a - 2,4,5; n - 2,3; d - 2; e - 2,3; r - 2; l - 3; o - 3,6; y - 3; s - 4; c - 6;
This is to be in c#. I've considered a 2d array, 26 (for the letters) x 20 (words in a sentence. The issue here is that my array is going to be sparse, and its also going to be hard work keeping track of which element is the next spare one against each letter. I'd want my array for the letter a to be [2,4,5] not [0,2,0,4,5] or [0,0,2,0,4,5], Its also complicated by wanting to cater for other symbols, so the 26 will get bigger quickly. The third of those arrays is the one that is "obvious" how to program, but is the least elegant solution.
static void Main(string[] args)
{
string[] sentence = new string[6] { "i", "wandered", "lonely", "as", "a", "cow" };
string alphabet = "abcdefghijklmnopqrstuvwxyz";
int[,] letterInWord= new int[26, 7];
for (int letterIndex = 0; letterIndex < alphabet.Length; letterIndex++)
{
for (int wordIndex = 0; wordIndex < sentence.Length; wordIndex++)
{
if(sentence[wordIndex].IndexOf(alphabet[letterIndex]) >= 0)
{
letterInWord[letterIndex, wordIndex+1] = wordIndex+1;
}
}
}
// then analyse or just print out (adding 1 to get counting base 1)
for (int letterIndex = 0; letterIndex < alphabet.Length; letterIndex++)
{
Console.Write(alphabet[letterIndex]+ " is in word(s) " );
for (int wordIndex = 1; wordIndex <= sentence.Length; wordIndex++)
{
if (letterInWord[letterIndex, wordIndex] > 0)
{
Console.Write(letterInWord[letterIndex, wordIndex] + " ");
}
}
Console.WriteLine();
}
}
So, that works, but I just don't like it.
Ideally i'd want a list for the sentence called sentenceList, and then for each letter i find (e.g. z), I'd look in sentenceList for a list called listForZ, and if i didnt find it, I'd create a new list called listForZ, add the word number to the List, and add listForZ into the sentenceList.
But that requires programmatically creating the name of the list from the variable I've just found in the word, and I've struggled to understand how that would work. I suppose I could use a factory method pattern which IS aware of all the listnames I could have and creates them appropriately, but again, that seems overkill for what I want.
Any suggested directions?
But that requires programmatically creating the name of the list from
the variable I've just found in the word, and I've struggled to
understand how that would work.
Using a Dictionary, you can associate a key with a value. In your case, the characters in the words are the keys, and the word positions where they occur are the values:
Dictionary<char, List<int>> occurrences = new Dictionary<char, List<int>>();
string sentence = "I wandered lonely as a cow";
string[] words = sentence.ToLower().Split(" ".ToCharArray());
for(int i = 0; i < words.Length; i++)
{
foreach(char c in words[i].ToCharArray().Distinct())
{
if (!occurrences.ContainsKey(c))
{
occurrences.Add(c, new List<int>());
}
occurrences[c].Add(i + 1);
}
}
foreach(KeyValuePair<char, List<int>> kvp in occurrences)
{
Console.WriteLine(kvp.Key.ToString() + " - " + String.Join(",", kvp.Value.ToArray()));
}
Output generated:
i - 1
w - 2,6
a - 2,4,5
n - 2,3
d - 2
e - 2,3
r - 2
l - 3
o - 3,6
y - 3
s - 4
c - 6
Using Regex :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace ConsoleApplication108
{
class Program
{
static void Main(string[] args)
{
string input = "I wandered lonely as a cow";
string pattern = #"(?'word'\w+)\s*";
string[] words = Regex.Matches(input, pattern).Cast<Match>().Select(x => x.Groups["word"].Value).ToArray();
var results = words
.Select(x => new { word = x, characters = x.ToCharArray().Select((y, i) => new { ch = y, index = i }).GroupBy(y => y.ch).Select(y => y.First()).ToList() }).ToList();
}
}
}
With a help of regular expressions (we have to match words) and Linq to query these words you can implement something like this:
string sentence = "I wandered lonely as a cow";
var result = string.Join("; ", Regex
.Matches(sentence, "[A-Za-z]+") // Word is a sequence of A..Z a..z letters
.OfType<Match>()
.Select((match, index) => new {
word = match.Value.ToLower(), // So we have word, e.g. "lonely"
index + 1 // and its index, e.g. "3"
})
.SelectMany(item => item.word.Select(c => new {
character = c, // for each character
wordNumber = item.index // we have a index of the word(s) where it appears
}))
.GroupBy(item => item.character, item => item.wordNumber) // grouping by character
.Select(chunk => $"{chunk.Key} - {string.Join(",", chunk.Distinct().OrderBy(n => n))}"));
// Let's have a look at the results
Console.Write(result);
Outcome:
i - 1; w - 2,6; a - 2,4,5; n - 2,3; d - 2; e - 2,3; r - 2; l - 3; o - 3,6; y - 3; s - 4; c - 6
The function gets a string of numbers (e.g. "23559009") and the length of substrings value (e.g. 2), I need to implement the function so that it will slice the string of numbers by the value (e.g. "23", "35", "55", "59", "90", "00", "09") AND will return this data as array.
For now I have initial code for tests:
using System;
public static class Series
{
public static string[] Slices(string numbers, int sliceLength)
{
int digits = numbers.Length;
if(digits != null || digits > sliceLength || sliceLength < 1)
throw new ArgumentException();
else
{
string[] dgts = {"1", "2"};
return dgts;
}
}
}
Using Linq:
public static string[] Slices(string numbers, int sliceLength) =>
Enumerable.Range(0, numbers.Length - sliceLength + 1).
Select(i => numbers.Substring(i, sliceLength)).
ToArray();
Note that the single character last entry will be ignored + you may want to validate the parameters (numbers not null and sliceLength > 0).
Fiddle
substring code for this will have major redundancy. send the string to a char array, then do a loop
char[] charray = inputstring.toCharArray();
List<string> deuces= new List<string>();
for(int i=0;i<charray.length;i++){
string holder = charray[i]+charray[i+1];
deuces.Add(holder)
}
keep in mind this is pseudo, everything you need is here, you will just have to create the variables, and make sure syntax is correct.
in the line : for(int i=0;i
the two represents the value you want to slice by,
in the line : string holder = charray[i]+charray[i+1];
you will need to add another char, for the amount of your split. i.e 3 would be:
string holder = charray[i].toString()+charray[i+1].toString+charray[i+2];
keep in mind if your split value ( in your case two) changes regularly you can nest another for loop
You have some errors in your evaluation of incorrect inputs, then getting your result in using normal for loop isn't difficult
public string[] Slices(string numbers, int sliceLength)
{
int digits = numbers.Length;
string[] result = new string[numbers.Length + 1 - sliceLength];
if (digits < sliceLength || sliceLength < 1)
throw new ArgumentException();
else
{
for(int x = 0; x < numbers.Length + 1 - sliceLength; x++)
result[x] = numbers.Substring(x, sliceLength);
return result;
}
}
I'm implementing a slightly fancier version of counting sort in C#. The "slightly fancier" part is that I replace some elements in the sorted output with "-" rather than the original value. Here is a sample input/ output pair (the range of possible integer values are between 0 and 99):
IN
20
0 ab
6 cd
0 ef
6 gh
4 ij
0 ab
6 cd
0 ef
6 gh
0 ij
4 that
3 be
0 to
1 be
5 question
1 or
2 not
4 is
2 to
4 the
OUT
- - - - - to be or not to be - that is the question - - - -
And here is my implementation:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
class Solution
{
static void Main(String[] args)
{
int n = Convert.ToInt32(Console.ReadLine());
List<List<string>> rsltLists = new List<List<string>>(100);
for(int i=0; i<n; i++)
{
rsltLists.Add(new List<String>()); // PROBLEM IS HERE
}
for(int a0 = 0; a0 < n; a0++)
{
string[] tokens_x = Console.ReadLine().Split(' ');
int x = Convert.ToInt32(tokens_x[0]);
string s = tokens_x[1];
if(a0 < n/2)
{
// Replace string with '-'
rsltLists[x].Add("-");
}
else
{
rsltLists[x].Add(s);
}
}
foreach(List<string> rsltList in rsltLists)
{
foreach(string rslt in rsltList)
{
Console.Write(rslt + " ");
}
}
}
}
I'm submitting my code as the solution to a problem on Hackerrank. The problem is that for the 5th test case, my solution times out (the test case contains an enormous number of lines so I'm not including it here). To speed my solution up, I replaced the //PROBLEM IS HERE line with rsltLists.Add(new List<String>(100)). This causes the 5th test case to fail rather than time out (test cases 1-4 still passed). When I replaced the problem line with rsltLists.Add(new List<String>(10000)) the 5th test case and several other test cases failed (though not all of the test cases failed). Why would changing the amount of space I reserve for each List<String> cause this inconsistent behavior? I would expected the fifth test case to fail (maybe), but I wouldn't have expected test cases that were passing previously to start failing.
Why are you creating n rsltLists? That is not the requirement. There are 100 possible values and array is better for that. You should NOT be using n here. x is 100.
for(int i=0; i<n; i++) // no, problem is here
{
rsltLists.Add(new List<String>()); // PROBLEM IS HERE
}
This should be pretty fast
public static string HackerSort()
{
List<string> input = new List<string>() {"20"
, "0 ab"
, "6 cd"
, "0 ef"
, "6 gh"
, "4 ij"
, "0 ab"
, "6 cd"
, "0 ef"
, "6 gh"
, "0 ij"
, "4 that"
, "3 be"
, "0 to"
, "1 be"
, "5 question"
, "1 or"
, "2 not"
, "4 is"
, "2 to"
, "4 the" };
List<string>[] wl = new List<string>[100];
int n = int.Parse(input[0]);
int half = n/2;
char split = ' ';
for (int i = 0; i < n; i++)
{
string s = input[i + 1];
string[] ss = s.Split(split);
//Debug.WriteLine(ss[0]);
int row = int.Parse(ss[0]);
if(wl[row] == null)
{
wl[row] = new List<string>((n / 100) + 1);
}
wl[row].Add(i < half ? "-" : ss[1]);
}
StringBuilder sb = new StringBuilder();
foreach(List<string> ls in wl.Where(x => x != null))
{
sb.Append(string.Join(" ", ls) + ' ');
}
Debug.WriteLine(sb.ToString().TrimEnd());
return sb.ToString().TrimEnd();
}
Couple thoughts on this:
You're creating a list for each option... but many aren't used. How about only instantiating the lists that you'll actually use?
Compounded with the one above, you're creating 100 lists, each with a capacity of 100... that's a lot of memory to set aside that you won't be using
One solution:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
class Solution
{
static void Main(String[] args)
{
int n = Convert.ToInt32(Console.ReadLine());
int threshold = n / 2;
List<string>[] stringMap = new List<string>[100];
for(int a0 = 0; a0 < n; a0++){
string[] tokens_x = Console.ReadLine().Split(' ');
int x = Convert.ToInt32(tokens_x[0]);
if(stringMap[x] == null)
{
stringMap[x] = new List<string>();
}
stringMap[x].Add((a0 >= threshold ? tokens_x[1] : "-"));
}
List<string> output = new List<string>();
for(int i = 0; i < stringMap.Length; i++)
{
if(stringMap[i] == null)
{
continue;
}
output.AddRange(stringMap[i]);
}
Console.WriteLine(string.Join(" ", output));
}
}
The "inner" List will always have exactly two elements, one of which you want to treat as a number rather than a string. Better to use a small class or even a tuple here, rather than nested lists.
I'm at work with only VS2015 with no tuple support, so this code is unchecked and likely has a mistake or two:
static void Main(String[] args)
{
int n = int.Parse(Console.ReadLine());
var data = new List<(int, string)>(n);
for(int a0 = 0; a0 < n; a0++)
{
var tokens = Console.ReadLine().Split(' ');
int x = int.Parse(tokens[0]);
if(a0 < n/2) tokens[1] = "-";
data.Add( (val: x, str: tokens[1]) )
}
foreach(var item in data.OrderBy(i => i.val))
{
Console.Write(item.str + " ");
}
}
One way to solve the memory-hogging / long processing times would be to store the input in a SortedDictionary<int, List<string>> instead. The Key would be the integer portion of the input, and the Value would be a List<string> containing the other part of the input (one item per input that matches the key).
Then, when we have the dictionary populated, we can just output each List<string> of data in order (the SortedDictionary will already be sorted by Key).
In this way, we're only creating as many lists as we actually need, and each list is only as long as it needs to be (both of which I believe were causes for the errors in your original code, but I don't know where to find the actual test case code to verify).
private static void Main()
{
var length = Convert.ToInt32(Console.ReadLine());
var halfway = length / 2;
var items = new SortedDictionary<int, List<string>>();
for (int inputLine = 0; inputLine < length; inputLine++)
{
var input = Console.ReadLine().Split();
var sortIndex = Convert.ToInt32(input[0]);
var value = inputLine < halfway ? "-" : input[1];
if (items.ContainsKey(sortIndex)
{
items[sortIndex].Add(value);
}
else
{
items.Add(sortIndex, new List<string> {value});
}
}
Console.WriteLine(string.Join(" ", items.SelectMany(i => i.Value)));
// Not submitted to website, but for local testing:
Console.Write("\n\nPress any key to exit...");
Console.ReadKey();
}
Output
I need to create code to scatter 50 random letters into the console (It's a "Snake" game); that part, I didn't have a problem with. I used this code to generate the letters (the "scatter them around the console" part I'm handling once I know how to fix the problem)
Random rand = new Random();
int number = rand.Next(0, 26);
char letter = (char)('a' + number);
It generates random letters just fine, but the instructions for the problem specify the ratio of consonants to vowels must be 4:1, and I have no idea how to make that part happen.
Create a pair of static strings:
String consonants = "BCDFGHJKLMNPQRSTVWXYZ";
String vowels = "AEIOU";
Generate a random number between 1..5 (or 0..4). If the number is 1 (0), pick a random character from the vowels list. Otherwise pick a random character from the consonants list.
Alternately, if you need exactly the ratio of 4:1, use a for-loop in place of the first random number generator, to wit:
for ( i = 0; i < 50; i++ )
{
if ( i % 5 == 0 )
// select a vowel at random
else
// select a consonant at random
}
EDIT: Complete solution. I'm writing my fifty characters to an array then printing them to the console. You can pass theChar to your output method.
public void RandomChars()
{
Random random = new Random();
String consonants = "BCDFGHJKLMNPQRSTVWXYZ";
String vowels = "AEIOU";
StringBuilder result = new StringBuilder();
for (int i = 0; i < 50; i++)
{
char theChar;
if (i % 5 == 0)
{
theChar = vowels[random.Next(vowels.Length)];
}
else
{
theChar = consonants[random.Next(consonants.Length)];
}
result.Append(theChar);
}
Console.WriteLine(result.ToString());
}