C# Get substring with specific pattern from string - c#

I have a list of strings like this:
List<string> list = new List<string>();
list.Add("Item 1: #item1#");
list.Add("Item 2: #item2#");
list.Add("Item 3: #item3#");
How can I get and add the substrings #item1#, #item2# etc into a new list?
I am only able to get the complete string if it contains a "#" by doing this:
foreach (var item in list)
{
if(item.Contains("#"))
{
//Add item to new list
}
}

You could have a look at Regex.Match. If you know a little bit about regular expressions (in your case it would be a quite simple pattern: "#[^#]+#"), you can use it to extract all items starting and ending with '#' with any number of other characters other than '#' in between.
Example:
Match match = Regex.Match("Item 3: #item3#", "#[^#]+#");
if (match.Success) {
Console.WriteLine(match.Captures[0].Value); // Will output "#item3#"
}

Here's another way using a regex with LINQ. (Not sure your exact requirements reference the regex, so now you may have two problems.)
var list = new List<string> ()
{
"Item 1: #item1#",
"Item 2: #item2#",
"Item 3: #item3#",
"Item 4: #item4#",
"Item 5: #item5#",
};
var pattern = #"#[A-za-z0-9]*#";
list.Select (x => Regex.Match (x, pattern))
.Where (x => x.Success)
.Select (x => x.Value)
.ToList ()
.ForEach (Console.WriteLine);
Output:
#item1#
#item2#
#item3#
#item4#
#item5#

LINQ would do the job nicely:
var newList = list.Select(s => '#' + s.Split('#')[1] + '#').ToList();
Or if you prefer query expressions:
var newList = (from s in list
select '#' + s.Split('#')[1] + '#').ToList();
Alternatively, you can use regular expressions as suggested with Botz3000 and combine those with LINQ:
var newList = new List(
from match in list.Select(s => Regex.Match(s, "#[^#]+#"))
where match.Success
select match.Captures[0].Value
);

The code will solve your problem.
But if the string does not contain #item# then the original string will be used.
var inputList = new List<string>
{
"Item 1: #item1#",
"Item 2: #item2#",
"Item 3: #item3#",
"Item 4: item4"
};
var outputList = inputList
.Select(item =>
{
int startPos = item.IndexOf('#');
if (startPos < 0)
return item;
int endPos = item.IndexOf('#', startPos + 1);
if (endPos < 0)
return item;
return item.Substring(startPos, endPos - startPos + 1);
})
.ToList();

How about this:
List<string> substring_list = new List<string>();
foreach (string item in list)
{
int first = item.IndexOf("#");
int second = item.IndexOf("#", first);
substring_list.Add(item.Substring(first, second - first);
}

You could do that by simply using:
List<string> list2 = new List<string>();
list.ForEach(x => list2.Add(x.Substring(x.IndexOf("#"), x.Length - x.IndexOf("#"))));

try this.
var itemList = new List<string>();
foreach(var text in list){
string item = text.Split(':')[1];
itemList.Add(item);
}

Related

Cut last character from string which was earlier splitted by char

I want to order list with string names by name included in brackes.
List<string> result = new List<string>();
list.ForEach(elem => result.Add(elem.Value));
result.Add(item);
result = result.OrderBy(o=>o.Split(';')[0].Substring(0, o.Length - 1).Split('(')[1]).ToList();
Example: 2-osobowy(Agrawka);Śniadanie+Obiadokolacja
I want to extract this name Agrawka
How to change instruction Substring(0, o.Length - 1)to cut last char from splitted string in orderby instruction?
If I right understood you want extract values in the brackets and sort input' list by that values. So code below sorts your data and extracts value to additional list:
List<string> resultList = new List<string>() { "2-osobowy(Bgrawka);Śniadanie+Obiadokolacja", "2-osobowy(Agrawka);Śniadanie+Obiadokolacja" };
string tempStr = null;
var extractedStr = new List<String>();
resultList = resultList.OrderBy(o =>
{
var extract = (tempStr = o.Split(';')[0].Split('(')[1]).Substring(0, tempStr.Length - 1);
extractedStr.Add(extract);
return extract;
}).ToList();
If you want only sort input data just simplify the lambda:
resultList = resultList.OrderBy(o => (tempStr = o.Split(';')[0].Split('(')[1]).Substring(0, tempStr.Length - 1)).ToList();

Finding number of instances of exact word of "x" in text

I'm working c# to find out number of instances of exact word of "x".
For example:
List<string> words = new List<string> {"Mode", "Model", "Model:"};
Text= "This is Model: x Type: y aa: e";
I've used Regex:
for(i=0; i<words.count; i++)
{
word= list[i]
int count= Regex.Matches(Text,word)
}
But its not working. The result of above code gave count=1 for every Mode, Model, and Model:.
I want to have my count to be 0 for Mode, 0 for Model, but 1 for Model: that it finds the number of instance of exact word.
Forgot that I can't use split in my case. Is there any way I can get not using split?
I use LINQ for this purpose:
List<string> words = new List<string> { "Mode", "Model", "Model:" };
Text = "This is Model: x Type: Model: y aa: Mode e Model:";
var textArray = Text.Split(' ');
var countt = words.Select(item => textArray.ToList().Contains(item) ?
textArray.Count(d => d == item) : 0).ToArray();
Result:
For Mode => count = 1
For Model => count = 0
For Model: => count = 3
EDIT: I prefer to use LINQ for this purpose because as you see it is more easier and cleaner in this scenario, but if you are looking for a Regex solution yet you could try this:
List<int> count = new List<int>();
foreach (var word in words)
{
var regex = new Regex(string.Format(#"\b{0}(\s|$)", word), RegexOptions.IgnoreCase);
count.Add(regex.Matches(Text).Count);
}
EDIT2: Or by combining LINQ and Regex and without Split you can:
List<int> count = words.Select(word => new Regex(string.Format(#"\b{0}(\s|$)", word), RegexOptions.IgnoreCase))
.Select(regex => regex.Matches(Text).Count).ToList();
Although #S.Akhbari 's solution works... I think using Linq is cleaner:
var splitted = Text.Split(' ');
var items = words.Select(x => new { Word = x, Count = splitted.Count(y => y == x) });
Each item will have Word and Count properties.
See it in action here
\b matches on word boundaries.
for(i=0; i<words.count; i++)
{
word= list[i]
var regex = new Regex(string.Format(#"\b{0}\b", word),
RegexOptions.IgnoreCase);
int count= regex.Matches(Text).Count;
}

Sort a List<string> comparing with a string

I need to sort a List<string> by comparing the list with a string
for example:
I have a List that contains the following Items.
Kaboki
kriiki
Kale
Kutta
Kiki
Kicki
Krishna
Kseaki
The search keyword is ki I need to sort the list items using the keyword in such a way that, the strings that match in the string start have should be first and the string having the matched string in the other position have to be in the last
Here is my current code
public static List<string> GetLocations(string prefixText)
{
try
{
DataTable dtlocs = (DataTable) HttpContext.Current.Session["locations"];
var dValue = from row in dtlocs.AsEnumerable()
where row.Field<string>("Location_Name").ToLower().Contains(prefixText.ToLower())
select row.Field<string>("Location_Name");
var results = dValue.OrderBy(s => s.IndexOf(prefixText, StringComparison.Ordinal));
var str = new List<string>(results.ToList());
if (!str.Any())
str.Add("No locations found");
return str;
}
catch (Exception)
{
var str = new List<string> {"No locations found"};
return str;
}
}
Here I'm able to get the first matched values to the top but cannot sort the remaining values
and I have another issue. there is a word King Koti and i'm searhing for Ko and this word comes to first.I think this happens because, the string has two sub strings and one of the substrings start with the matched word.
and can I make the matched letters to bold ??
var res = list.OrderBy(y=> !y.StartsWith("Ki", StringComparison.OrdinalIgnoreCase))
.ThenBy(x => x)
OrderBy orders false before true:
var result = list.OrderBy(s => !s.StartsWith("ki", StringComparison.OrdinalIgnoreCase))
.ThenBy(s => !s.ToLower().Contains("ki"));
I think this should work:
list = (from str in list
let idx = str.IndexOf(keyword, StringComparison.OrdinalIgnoreCase)
let change = idx != 0 ? idx : int.MinValue
orderby change
select str).ToList();
You can use a combination of Linq's OrderBy and the IndexOf methods:
var input = ...
var search = "ki";
var results = input.Select(Value => new { Value, Index = s.IndexOf(search, StringComparison.InvariantCultureIgnoreCase) })
.Where(pair => pair.Index >= 0)
.OrderBy(pair => pair.Index)
.Select(pair => pair.Value);
Or in query syntax:
var results =
from s in input
let i = s.IndexOf(search, StringComparison.InvariantCultureIgnoreCase)
where i >= 0
orderby i
select s;

Given collection of strings, count number of times each word appears in List<T>

Input 1: List<string>, e.g:
"hello", "world", "stack", "overflow".
Input 2: List<Foo> (two properties, string a, string b), e.g:
Foo 1:
a: "Hello there!"
b: string.Empty
Foo 2:
a: "I love Stack Overflow"
b: "It's the best site ever!"
So i want to end up with a Dictionary<string,int>. The word, and the number of times it appears in the List<Foo>, either in the a or the b field.
Current first-pass/top of my head code, which is far too slow:
var occurences = new Dictionary<string, int>();
foreach (var word in uniqueWords /* input1 */)
{
var aOccurances = foos.Count(x => !string.IsNullOrEmpty(x.a) && x.a.Contains(word));
var bOccurances = foos.Count(x => !string.IsNullOrEmpty(x.b) && x.b.Contains(word));
occurences.Add(word, aOccurances + bOccurances);
}
Roughly:
Build a dictionary (occurrences) from the first input, optionally with a case-insensitive comparer.
For each Foo in the second input, use RegEx to split a and b into words.
For each word, check if the key exists in occurrences. If it exists, increment and update the value in the dictionary.
You could try concating the two strings a + b. Then doing a regex to pull out all the words into a collection. Then finally indexing that using a group by query.
For example
void Main()
{
var a = "Hello there!";
var b = "It's the best site ever!";
var ab = a + " " + b;
var matches = Regex.Matches(ab, "[A-Za-z]+");
var occurences = from x in matches.OfType<System.Text.RegularExpressions.Match>()
let word = x.Value.ToLowerInvariant()
group word by word into g
select new { Word = g.Key, Count = g.Count() };
var result = occurences.ToDictionary(x => x.Word, x => x.Count);
Console.WriteLine(result);
}
Example with some changes suggested...
Edit. Just reread the requirement....kinda strange but hey...
void Main()
{
var counts = GetCount(new [] {
"Hello there!",
"It's the best site ever!"
});
Console.WriteLine(counts);
}
public IDictionary<string, int> GetCount(IEnumerable<Foo> inputs)
{
var allWords = from input in inputs
let matchesA = Regex.Matches(input.A, "[A-Za-z']+").OfType<System.Text.RegularExpressions.Match>()
let matchesB = Regex.Matches(input.B, "[A-Za-z']+").OfType<System.Text.RegularExpressions.Match>()
from x in matchesA.Concat(matchesB)
select x.Value;
var occurences = allWords.GroupBy(x => x, (x, y) => new{Key = x, Count = y.Count()}, StringComparer.OrdinalIgnoreCase);
var result = occurences.ToDictionary(x => x.Key, x => x.Count, StringComparer.OrdinalIgnoreCase);
return result;
}

Mapping numbers to letters

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);
}
}

Categories

Resources