Related
I have a list of strings and a char array of letters. And I want to display the strings that contain all the chars from the array in the string.
List-list of strings
charsArray-array of chars
Final-the final list with the sorted words
Example
charsArray {'a','e','i','z'}
List {"abcde","iertaz","aio","zzeml","amoioze"}
Final(output) iertaz amoioze
I tried this
foreach(string word in List)
{
bool contain = true;
foreach (char letter in charsArray}
{
if (word.Contains(letter) && contain)
{
Final.Add(word);
}
else
{
contain = false;
}
}
}
foreach (string word in Final)
{
Console.WriteLine(word);
}
Having
char[] chars = new char[] {
'a','e','i','z'};
List<string> list = new List<string>() {
"abcde", "iertaz", "aio", "zzeml", "amoioze" };
We can try subtracting word from chars: if all characters from chars are removed, the word is what we want:
using System.Linq;
...
var result = list
.Where(word => !chars.Except(word).Any())
.ToList();
This solution uses All:
var charsArray = new char[] { 'a', 'e', 'i', 'z' };
var L = new[] { "abcde","iertaz","aio","zzeml","amoioze"}.ToList();
var final = L.Where(word => charsArray.All(a => word.Contains(a))).ToList();
The result is correct:
iertaz
amoioze
How about this, demonstrated here
public static IEnumerable<IEnumerable<T>> ContainsAll<T>(
this IEnumerable<IEnumerable<T>> source,
IEnumerable<T> search,
IEqualityComparer<T> comparer = default)
{
ArgumentNullException.ThrowIfNull(source);
ArgumentNullException.ThrowIfNull(search);
comparer ??= EqualityComparer<T>.Default;
var searchSet = search is HashSet<T> ss && ss.Comparer == comparer
? ss
: new HashSet<T>(search, comparer);
return source.Where(item => searchSet.IsSubsetOf(item));
}
That you can use like this,
var words = new[] {"abcde","iertaz","aio","zzeml","amoioze"};
var search = new[] {'a','e','i','z'};
foreach (string match in words.ContainsAll(search))
{
Console.WriteLine(match);
}
In this line you are adding the word if the first character is in the word because contain is true
...
if (word.Contains(letter) && contain)
{
Final.Add(word);
}
...
you are almost there, you just need a little modification to avoid the mistake above
foreach(string word in List)
{
bool contain = true;
foreach (char letter in charsArray}
{
if (!word.Contains(letter))
{
contain = false;
}
}
if (contain)
{
Final.Add(word);
}
}
foreach (string word in Final)
{
Console.WriteLine(word);
}
You want to find strings that contain all the chars from charsArray. It means every char in charsArray should be contained by the target string.
using System.Linq;
var List = new List<string>() { "abcde", "iertaz", "aio", "zzeml", "amoioze" };
var charsArray = new char[] { 'a','e','i','z' };
var result = List.FindAll(word => charsArray.All(letter => word.Contains(letter)));
I want to find two longest words from array ,made from smaller words. my code are given below.
current out put is:
catxdogcatsrat, ratcatdogcat, catsdogcats, dogcatsdog
required output is:
ratcatdogcat, catsdogcats
class program
{
public static void Main(String[] args)
{
List<string> list2 = new List<string>();
string[] stringrray = { "cat", "cats", "catsdogcats", "catxdogcatsrat", "dog", "dogcatsdog",
"hippopotamuses", "rat", "ratcatdogcat" };
list2.Add(stringrray[0]);
list2.Add(stringrray[1]);
list2.Add(stringrray[2]);
list2.Add(stringrray[3]);
list2.Add(stringrray[4]);
list2.Add(stringrray[5]);
list2.Add(stringrray[6]);
list2.Add(stringrray[7]);
list2.Add(stringrray[8]);
List<string> list = new List<string>();
var mod = list2.OrderByDescending(x => x.Length).ToList();
int j = 1;
for (int k = 0; k < mod.Count; k++)
{
for (int i = 0; i < mod.Count-j; i++)
{
if (mod[i].Contains(mod[mod.Count - j]))
{
j++;
list.Add(mod[i]);
}
}
}
var mod1 = list.OrderByDescending(x => x.Length);
foreach (var i in mod1)
{
Console.WriteLine(i);
}
Console.ReadLine();
}
}
I think you are looking for something like this
string[] stringrray = { "cat", "cats", "catsdogcats", "catxdogcatsrat", "dog", "dogcatsdog", "hippopotamuses", "rat", "ratcatdogcat" };
List<string> list2 = new List<string>(stringrray);
List<string> Finallist = new List<string>();
char[] smallstrchar = String.Join("", list2.Where(x => x.Length <= 4)).ToCharArray();
char[] bigstrchar = String.Join("", list2.Where(x => x.Length > 4)).ToCharArray();
char[] modchar = bigstrchar.Except(smallstrchar).ToArray();
foreach(string bigstr in list2)
{
if(!(bigstr.IndexOfAny(modchar) != -1))
{
Finallist.Add(bigstr);
}
}
Finallist = Finallist.OrderByDescending(x => x.Length).Take(2).ToList();
foreach(string finalstr in Finallist)
{
Console.WriteLine(finalstr);
}
So first is the stringrray which contains all the strings which are supposed to be taken care and find the longest one out of it. With your code it also takes the string which contains x in them but all other chars are matched. So I have made a list of strings in list2 which contains all the values. Then splitted the list2 in 2 parts that is smallstrchar array contains all the chars of the smaller strings less than length of 4 and Bigstrchar contains all chars of strings which are bigger than length of 5. Now Except takes out all the chars which does not exsist in the smallstrchar and present in Bigstrchar. Now we have the list of chars which need to be excluded from the sort.
Finally IndexOfAny to find in that string contains that char or not. If not then add to Finallist. Later we can take 2 from the list.
Hope this helps
You could simplify adding the array to list2 with
list2.AddRange(stringrray)
You may use this code...
static void Main(string[] args)
{
List<string> words = new List<string>() { "cat", "cats", "catsdogcats", "catxdogcatsrat", "dog", "dogcatsdog", "hippopotamuses", "rat", "ratcatdogcat" };
List<string> result = new List<string>();
// solution 1
foreach (string word in words)
{
if (IsCombinationOf(word, words))
{
result.Add(word);
}
}
// solution 2
result = words.Where(x => IsCombinationOf(x, words)).ToList();
}
public static bool IsCombinationOf(string word, List<string> parts)
{
// removing the actual word just to be secure.
parts = parts.Where(x => x != word).OrderByDescending(x => x.Length).ToList();
// erase each part in word. Only those which are not in the list will remain.
foreach (string part in parts)
{
word = Regex.Replace(word, part, "");
}
// if there are any caracters left, it hasn't been a combination
return word.Length == 0;
}
but...
This code has a little bug. The OrderbyDescending clause ensures that cats will be removed before cat. Otherwise the s would remain and the code wouldn't work as expected. But if we use some fictional values this code will not work properly. For example:
List<string> words = new List<string>() { "abz", "e", "zefg", "f", "g", "abzefg" };
Let's have a look at abzef. The algorithm will remove zefg first, but then it's not possible to go any futher. Indeed, the word is a combination of abz, e, f and g .
If we have a list of strings, then how we can find the list of strings that have the maximum number of repeated symbol by using LINQ.
List <string> mylist=new List <string>();
mylist.Add("%1");
mylist.Add("%136%250%3"); //s0
mylist.Add("%1%5%20%1%10%50%8%3"); // s1
mylist.Add("%4%255%20%1%14%50%8%4"); // s2
string symbol="%";
List <string> List_has_MAX_num_of_symbol= mylist.OrderByDescending(s => s.Length ==max_num_of(symbol)).ToList();
//the result should be a list of s1 + s2 since they have **8** repeated '%'
I tried
var longest = mylist.Where(s => s.Length == mylist.Max(m => m.Length)) ;
this gives me only one string not both
Here's a very simple solution, but not exactly efficient. Every element has the Count operation performed twice...
List<string> mylist = new List<string>();
mylist.Add("%1");
mylist.Add("%136%250%3"); //s0
mylist.Add("%1%5%20%1%10%50%8%3"); // s1
mylist.Add("%4%255%20%1%14%50%8%4"); // s2
char symbol = '%';
var maxRepeat = mylist.Max(item => item.Count(c => c == symbol));
var longest = mylist.Where(item => item.Count(c => c == symbol) == maxRepeat);
It will return 2 strings:
"%1%5%20%1%10%50%8%3"
"%4%255%20%1%14%50%8%4"
Here is an implementation that depends upon SortedDictionary<,> to get what you're after.
var mylist = new List<string> {"%1", "%136%250%3", "%1%5%20%1%10%50%8%3", "%4%255%20%1%14%50%8%4"};
var mappedValues = new SortedDictionary<int, IList<string>>();
mylist.ForEach(str =>
{
var count = str.Count(c => c == '%');
if (mappedValues.ContainsKey(count))
{
mappedValues[count].Add(str);
}
else
{
mappedValues[count] = new List<string> { str };
}
});
// output to validate output
foreach (var str in mappedValues.Last().Value)
{
Console.WriteLine(str);
}
Here's one using LINQ that gets the result you're after.
var result = (from str in mylist
group str by str.Count(c => c == '%')
into g
let max = (from gKey in g select g.Key).Max()
select new
{
Count = max,
List = (from str2 in g select str2)
}).LastOrDefault();
OK, here's my answer:
char symbol = '%';
var recs = mylist.Select(s => new { Str = s, Count = s.Count(c => c == symbol) });
var maxCount = recs.Max(x => x.Count);
var longest = recs.Where(x => x.Count == maxCount).Select(x => x.Str).ToList();
It is complicated because it has three lines (the char symbol = '%'; line excluded), but it counts each string only once. EZI's answer has only two lines, but it is complicated because it counts each string twice. If you really want a one-liner, here it is:
var longest = mylist.Where(x => x.Count(c => c == symbol) == mylist.Max(y => y.Count(c => c == symbol))).ToList();
but it counts each string many times. You can choose whatever complexity you want.
We can't assume that the % is always going to be the most repeated character in your list. First, we have to determine what character appears the most in an individual string for each string.
Once we have the character and it maximum occurrence, we can apply Linq to the List<string> and grab the strings that contain the character equal to its max occurrence.
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
List <string> mylist=new List <string>();
mylist.Add("%1");
mylist.Add("%136%250%3");
mylist.Add("%1%5%20%1%10%50%8%3");
mylist.Add("%4%255%20%1%14%50%8%4");
// Determine what character appears most in a single string in the list
char maxCharacter = ' ';
int maxCount = 0;
foreach (string item in mylist)
{
// Get the max occurrence of each character
int max = item.Max(m => item.Count(c => c == m));
if (max > maxCount)
{
maxCount = max;
// Store the character whose occurrence equals the max
maxCharacter = item.Select(c => c).Where(c => item.Count(i => i == c) == max).First();
}
}
// Print the strings containing the max character
mylist.Where(item => item.Count(c => c == maxCharacter) == maxCount)
.ToList().ForEach(Console.WriteLine);
}
}
Results:
%1%5%20%1%10%50%8%3
%4%255%20%1%14%50%8%4
Fiddle Demo
var newList = myList.maxBy(x=>x.Count(y=>y.Equals('%'))).ToList();
This should work. Please correct syntax if wrong anywhere and update here too if it works for you.
i write a c# program that read data from 5 text files and count them according to some given key word
string[] word_1 = File.ReadAllText(#"C:\Users\Niyomal N\Desktop\Assignment\Assignment\D1_H1.txt").Split(' ');
string[] word_2 = File.ReadAllText(#"C:\Users\Niyomal N\Desktop\Assignment\Assignment\D2_H1.txt").Split(' ');
string[] word_3 = File.ReadAllText(#"C:\Users\Niyomal N\Desktop\Assignment\Assignment\D3_H2.txt").Split(' ');
string[] word_4 = File.ReadAllText(#"C:\Users\Niyomal N\Desktop\Assignment\Assignment\D4_H2.txt").Split(' ');
string[] word_5 = File.ReadAllText(#"C:\Users\Niyomal N\Desktop\Assignment\Assignment\D5_H2.txt").Split(' ');
string[] given_doc = File.ReadAllText(#"C:\Users\Niyomal N\Desktop\Assignment\Assignment\Given_doc.txt").Split(' ');
this is how i read from text files, after reading that i use for loop and if loop to count each word from hose file
for (int i = 0; i < word_1.Length; i++)
{
string s = word_1[i];
if ("Red".Equals(word_1[i]))
{
//Console.WriteLine(word[i]);
h1_r++;
}
if ("Green".Equals(word_1[i]))
{
h1_g++;
}
if ("Blue".Equals(word_1[i]))
{
h1_b++;
}
}
this is the loop i used to get the count from one file and its works fine, i did this process 5 times to read all files, my question is how can i read those 5 files using one for loop and store them in a array (count of each key word)
thanks in advance !!
LINQ query is your the simplest solution here:
var filenames = new[] { "D1_H1.txt", "D2_H1.txt", "D3_H2.txt" };
var words = new[] { "Red", "Green", "Blue" };
var counters =
filenames.Select(filename => Path.Combine(#"C:\Users\Niyomal N\Desktop\Assignment\Assignment", filename))
.SelectMany(filepath => File.ReadAllLines(filepath))
.SelectMany(line => line.Split(new[] { ' ' }))
.Where(word => words.Contains(word))
.GroupBy(word => word, (key, values) => new
{
Word = key,
Count = values.Count()
})
.ToDictionary(g => g.Word, g => g.Count);
and then you have dictionary of word counter within all files:
int redCount = counters["Red"];
If you want to store counters per each file, you can use slightly modified query:
var filenames = new[] { "D1_H1.txt", "D2_H1.txt", "D3_H2.txt" };
var words = new[] { "Red", "Green", "Blue" };
var counters =
filenames.Select(filename => Path.Combine(#"C:\Users\Niyomal N\Desktop\Assignment\Assignment", filename))
.Select(filepath => new
{
Filepath = filepath,
Count = File.ReadAllLines(filepath)
.SelectMany(line => line.Split(new[] { ' ' }))
.Where(word => words.Contains(word))
.GroupBy(word => word, (key, values) => new
{
Word = key,
Count = values.Count()
})
.ToDictionary(g => g.Word, g => g.Count)
})
.ToDictionary(g => g.Filepath, g => g.Count);
and then use it accordingly:
int redCount = counters[#"C:\Users\(...)\D1_H1.txt"]["Red"];
Copy pasting code is generally not good. It leads to code violating the Don't Repeat Yourself (DRY) rule. Restructure your code:
const string path = #"C:\Users\Niyomal N\Desktop\Assignment\Assignment";
string[] files = new string[] { "D1_H1.txt", "D2_H1.txt", "D3_H1.txt", ... };
foreach (string file in files) {
string fullPath = Path.Combine(path, file);
//TODO: count words of file `fullPath`
}
Storing the word counts in an array is not optimal as you will have to traverse the array for each word you are encountering in a file.
Use a dictionary instead which has a constant lookup time. That's much faster.
var wordCount = new Dictionary<string, int>();
You can then count the words like this:
int count;
if (wordCount.TryGetValue(word, out count)) {
wordCount[word] = count + 1;
} else {
wordCount[word] = 1;
}
UPDATE
You can test for keywords like this
var keywords = new HashSet<string> { "Red", "Green", "Blue" };
string word = "Green";
if (keywords.Contains(word)) {
...
}
HasSets are as fast as dictionaries.
Be careful with the word casing. HashSets are case sensitive by default. If "red" and "Red" and "RED" have to be found alltogehter, initialize the HashSet like this:
var keywords = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase)
{ "Red", "Green", "Blue" };
List<KeyValuePair<string, string>> completeList = new List<KeyValuePair<string, string>>();
completeList.AddRange("D1_H1.txt",File.ReadAllText(#"C:\Users\Niyomal N\Desktop\Assignment\Assignment\D1_H1.txt").Split(' '));
completeList.AddRange("D1_H2.txt", File.ReadAllText(#"C:\Users\Niyomal N\Desktop\Assignment\Assignment\D2_H1.txt").Split(' '));
completeList.AddRange("D1_H3.txt", File.ReadAllText(#"C:\Users\Niyomal N\Desktop\Assignment\Assignment\D3_H2.txt").Split(' '));
completeList.AddRange("D1_H4.txt", File.ReadAllText(#"C:\Users\Niyomal N\Desktop\Assignment\Assignment\D4_H2.txt").Split(' '));
completeList.AddRange("D1_H5.txt", File.ReadAllText(#"C:\Users\Niyomal N\Desktop\Assignment\Assignment\D5_H2.txt").Split(' '));
completeList.AddRange("D1_H6.txt", File.ReadAllText(#"C:\Users\Niyomal N\Desktop\Assignment\Assignment\Given_doc.txt").Split(' '));
var result = completeList.GroupBy(r => r.Key).Select(r => new {File = r.Key, Red = r.Count(s => s.Value == "red"), Green = r.Count(s => s.Value == "green"), Blue = r.Count(s => s.Value == "blue") });
foreach (var itm in result)
{
Console.WriteLine(itm.File);
Console.WriteLine(itm.Red);
Console.WriteLine(itm.Green);
Console.WriteLine(itm.Blue);
}
I had an interview question asking this:
text file has following lines>
1: A C D
4: A B
5: D F
7: A E
9: B C
*Every line has a unique integer followed by a colon and one or
more letters. These letters are
delimited spaces (one or more)>
#2 Write a short program in the language
of your choice that outputs a sorted
list like
A: 1 4 7
B: 4 9
C: 1 9
D: 1 5
E: 7
F: 5
I'm not looking for someone to solve it, but I always get confused with problems like this. I'd like to do it in C# and was wondering should I store each line in a 2d array? What is the best way to handle this. After storing it how do I relist each line with letters rather then numbers?
Just looking for pointers here.
You can solve the problem by creating a Lookup mapping letters to a collection of numbers. You can use the extension method ToLookup to create a Lookup.
Warning: Spoilers ahead
Using LINQ you can do it like this (breaks on invalid input):
var text = #"1: A C D
4: A B
5: D F
7: A E
9: B C";
var lookup = text
.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
.Select(
line => new {
Number = Int32.Parse(line.Split(':').First()),
Letters = line.Split(':').Skip(1).First().Split(
new[] {' '}, StringSplitOptions.RemoveEmptyEntries
)
}
)
.SelectMany(x => x.Letters, (x, letter) => new { x.Number, Letter = letter })
.OrderBy(x => x.Letter)
.ToLookup(x => x.Letter, x => x.Number);
foreach (var item in lookup)
Console.WriteLine(item.Key + ": " + String.Join(" ", item.ToArray()));
In case you are familiar with LINQ the below code can give you what you are looking for:
var result = File.ReadAllLines("inFile").SelectMany(line =>
{
var ar = line.Split(" ".ToCharArray());
var num = int.Parse(ar[0].Split(":".ToCharArray())[0]);
return ar.Skip(1).Select(s => new Tuple<string, int>(s, num));
}).GroupBy(t => t.Item1).OrderByDescending(g => g.Count())
.Select(g => g.Key + ": " + g.Select(t => t.Item2.ToString()).Aggregate( (a,b) => a + " " + b));
File.WriteAllLines("outFile", result);
I know you said you didn't want full answers, but this kind of thing is fun. It looks like others have come up with similar solutions, but here's another way to represent it - in "one line" of code (but lots of brackets!) :)
var data = #"1: A C D
4: A B
5: D F
7: A E
9: B C";
Console.WriteLine(
String.Join(
Environment.NewLine,
(from line in data.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
let lineParts = line.Split(new[] { ':', ' ' }, StringSplitOptions.RemoveEmptyEntries)
from letter in lineParts.Skip(1)
select new { Number = lineParts[0], Letter = letter })
.ToLookup(l => l.Letter, l => l.Number)
.OrderBy(l => l.Key)
.Select(l => String.Format("{0}: {1}", l.Key, String.Join(" ", l)))));
Oh, and would I write code like that in production? Probably not, but it's fun in an exercise like this!
The thing that will help you solve this
IDictionary<char, IList<int> >
Yet Another Linq Masturbatory Implementation ("Look Ma! No loops!")
using System;
using System.IO;
using System.Linq;
public static class Program
{
public static void Main(string[] args)
{
File.ReadAllLines("input.txt")
.Select(line =>
{
var split = line.Split(":".ToCharArray(), 2);
return new { digit = split[0].Trim().Substring(0,1),
chars = split[1]
.Split(" \t".ToCharArray())
.Select(s=>s.Trim())
.Where(s => !String.IsNullOrEmpty(s))
.Select(s => s[0])
};
})
.SelectMany(p => p.chars.Select(ch => new { p.digit, ch }))
.GroupBy(p => p.ch, p => p.digit)
.ToList()
.ForEach(g => Console.WriteLine("{0}: {1}", g.Key, string.Join(" ", g)));
}
}
Of course you can replace GroupBy with ToLookup
I will use a Dictionary<string,List<int>> I will read the input and add 1 into the list at keys A,C,D, A at keys A,B etc, so having the result is just a lookup by letter.
So like this, in a non esoteric way:
string inp = #"1: A C D
4: A B
5: D F
7: A E
9: B C";
Dictionary<string, List<int>> res = new Dictionary<string, List<int>>();
StringReader sr = new StringReader(inp);
string line;
while (null != (line = sr.ReadLine()))
{
if (!string.IsNullOrEmpty(line))
{
string[] tokens = line.Split(": ".ToArray(),StringSplitOptions.RemoveEmptyEntries);
int idx = int.Parse(tokens[0]);
for (int i = 1; i < tokens.Length; ++i)
{
if (!res.ContainsKey(tokens[i]))
res[tokens[i]] = new List<int>();
res[tokens[i]].Add(int.Parse(tokens[0]));
}
}
}
res will contain the result of letter->list of numbers.
String parsing using Split(":") and Split(" ").
Then fill
Dictionary<int, List<string>>
and translate it into
Dictionary<string, List<int>>
You could store the input in an IDictionary, and reverse it to produce your output.
Take a look at this question.
I see that multiple similar (loops) and not so similar (linq) solutions were already posted but since i've written this i thought i'd throw it in the mix.
static void Main(string[] args)
{
var result = new SortedDictionary<char, List<int>>();
var lines = System.IO.File.ReadAllLines(#"input.txt");
foreach (var line in lines)
{
var split = line.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
var lineNumber = Int32.Parse(split[0].Substring(0,1));
foreach (var letter in split.Skip(1))
{
var key = letter[0];
if (!result.ContainsKey(key))
{
result.Add(key, new List<int> { lineNumber });
}
else
{
result[key].Add(lineNumber);
}
}
}
foreach (var item in result)
{
Console.WriteLine(String.Format("{0}: {1}", item.Key, String.Join(" ", item.Value)));
}
Console.ReadKey();
}
An important part of the interview process is asking about and verifying assumptions. Although your description states the file is structured as an integer followed by letters, the example you give shows the integers in increasing order. If that's the case, you can avoid all of the LINQ craziness and implement a much more efficient solution:
var results = new Dictionary<char, List<int>>();
foreach (var line in File.ReadAllLines(#"input.txt"))
{
var split = line.Split(new []{' '}, StringSplitOptions.RemoveEmptyEntries);
var num = int.Parse(split[0].TrimEnd(':'));
for (int i = 1; i < split.Length; i++)
{
char letter = split[i][0];
if (!results.ContainsKey(letter))
results[letter] = new List<int>();
results[letter].Add(num);
}
}