I need to search a list for some pattern is present or not.
var result=roles.Where(z=>z.Contains(x) && z.Contains(y)).ToList();
string x = "Resource:resource1:resource2";
string y = "writer";
List<string> roles=new List<string>{"Resource::reader","Resource:resource1::Deleter","Resource:resource1::writer"};
I need to find if any value is present in roles list like:
Resource::writer or Resource:resource1::writer or
Resource:resource1:resource2::writer
i.e Split x based on : and append y to the combination of splitted x
If my understanding of your problem is right :
You have a list which can contain anything that you names roles. Thoses roles are in format A::B or A:B::C or A:B:C::D etc...
And what you want to achieve is to find if any "path" or combination of path from x can give the role y ?
for instance : if you have roles like A::Z A::Y A:B::X A:B:C::X
you have x which is A:B:C
and you have y which is X
you want to check is you have A::X in the list
if you don't, you're gonna check A:B::X in the list,
and if you still don't, you will look for A:B:C::X
So again if I'm right, you could consider something like this :
String path = "A:B:C";
String roleNeeded = "X";
List<String> roles = new List<string>() { "A::Z", "A::Y", "A:B::X" };
List<String> pathStep = new List<string>();
pathStep = path.Split(':').ToList();
String lookupPath = String.Empty;
String result = String.Empty;
pathStep.ForEach( s =>
{
lookupPath += s;
if (roles.Contains(lookupPath + "::" + roleNeeded))
{
result = lookupPath + "::" + roleNeeded;
}
lookupPath += ":";
});
if (result != String.Empty)
{
// result is Good_Path::Role
}
This way you start spliting your path X as a list and you aggregate it in the foreach to look at each step.
You should consider using Regular Expression. Try this out,
string x = "Resource:resource1:resource2";
string y = "writer";
List<string> roles;
List<string> words = new List<string> { x, y };
// We are using escape to search for multiple strings.
string pattern = string.Join("|", words.Select(w => Regex.Escape(w)));
Regex regex = new Regex(pattern, RegexOptions.IgnoreCase);
// You got matched results...
List<string> matchedResults = roles.Where(regex.IsMatch).ToList();
string x = "Resource:resource1:resource2";
string y = "writer";
List<string> roles = new List<string>
{
"Resource::writer",
"Resource:resource1:resource2::writer"
};
var records = x.Split(':').Select((word, index) => new { word, index });
var result =
from record in records
let words = $"{string.Join(":", records.Take(record.index + 1).Select(r => r.word))}::{y}"
join role in roles on words equals role
select words;
Related
I have a problem finding the next integer match in a list of strings, there are some other aspects to consider:
single string contains non relevant trailing and leading chars
numbers are formatted "D6" example 000042
there are gaps in the numbers
the list is not sorted, but it could be if there is a fast way to ignore the leading chars
Example:
abc-000001.file
aaac-000002.file
ab-002010.file
abbc-00003.file
abbbc-00004.file
abcd-00008.file
abc-000010.file
x-902010.file
The user input is 7 => next matching string would be abcd-000008.file
My attempt is :
int userInput = 0;
int counter = 0;
string found = String.Empty;
bool run = true;
while (run)
{
for (int i = 0; i < strList.Count; i++)
{
if(strList[i].Contains((userInput + counter).ToString("D6")))
{
found = strList[i];
run = false;
break;
}
}
counter++;
}
It's bad because it's slow and it can turn into a infinite loop. But I really don't know how to do this (fast).
You can parse numbers from strings with Regex and created a sorted collection which you can search with Where clause:
var strings = new[] { "abc-000001.file", "x-000004.file"};
var regP = "\\d{6}"; // simplest option in my example, maybe something more complicated will be needed
var reg = new Regex(regP);
var collection = strings
.Select(s =>
{
var num = reg.Match(s).Captures.First().Value;
return new { num = int.Parse(num), str = s};
})
.OrderBy(arg => arg.num)
.ToList();
var userInput = 2;
var res = collection
.Where(arg => arg.num >= userInput)
.FirstOrDefault()?.str; // x-000004.file
P.S.
How 9002010, 0000010, 0002010 should be treated? Cause they have 7 characters. Is it [9002010, 10, 2010] or [900201, 1, 201]?
If you don't want regex, you can do something like that:
List<string> strings = new List<string>
{
"abc-000001.file",
"aaac-000002.file",
"ab-0002010.file",
"abbc-000003.file",
"abbbc-000004.file",
"abcd-000008.file"
};
int input = 7;
var converted = strings.Select(s => new { value = Int32.Parse(s.Split('-', '.')[1]), str = s })
.OrderBy(c => c.value);
string result = converted.FirstOrDefault(v => v.value >= input)?.str;
Console.WriteLine(result);
Hello I looked at several post about this topics but no answer could help me.
I extract data about various machines which look like this:
"time, M1.A, M1.B, M1.C, M2.A, M2.B, M2.C, M3.A, M3.B, M3.C"
M1 is the prefix which specifies which machine. A,B,C are attributes of this machine like temperature, pressure, etc.
The output should then look like this:
{{"time", "M1.A", "M1.B", "M1.C"}, {"time", "M2.A",....}}
I know that I could possibly split at "," and then create the list but I was wondering if there is another way to detect if the prefix changed.
Regex.Matches(myList, #"M(?<digit>\d+)\..") //find all M1.A etc
.Cast<Match>() //convert the resulting list to an enumerable of Match
.GroupBy(m => m.Groups["digit"].Value) //find the groups with the same digits
.Select(g => new[] { "time" }.Union(g.Select(m => m.Value)).ToArray());
//combine the groups into arrays beginning with "time"
You mention "the output should then look like this...", but then you mention a list, so I'm going to assume that you mean to make the original string into a list of lists of strings.
List<string> split = new List<string>(s.Split(','));
string first = split[0];
split.RemoveAt(0);
List<List<string>> result = new List<List<string>>();
foreach (var dist in split.Select(o => o.Split('.')[0]).Distinct())
{
List<string> temp = new List<string> {first};
temp.AddRange(split.Where(o => o.StartsWith(dist)));
result.Add(temp);
}
This does the original split, removes the first value (you didn't really specify that, I assumed), then loops around each machine. The machines are created by splitting each value further by '.' and making a distinct list. It then selects all values in the list that start with the machine and adds them with the first value to the resulting list.
Using Regex I created a dictionary :
string input = "time, M1.A, M1.B, M1.C, M2.A, M2.B, M2.C, M3.A, M3.B, M3.C";
string pattern1 = #"^(?'name'[^,]*),(?'machines'.*)";
Match match1 = Regex.Match(input, pattern1);
string name = match1.Groups["name"].Value;
string machines = match1.Groups["machines"].Value.Trim();
string pattern2 = #"\s*(?'machine'[^.]*).(?'attribute'\w+)(,|$)";
MatchCollection matches = Regex.Matches(machines, pattern2);
Dictionary<string, List<string>> dict = matches.Cast<Match>()
.GroupBy(x => x.Groups["machine"].Value, y => y.Groups["attribute"].Value)
.ToDictionary(x => x.Key, y => y.ToList());
Some quick example for you. I think is better to parse it by you own way and have string structure of your Machine-Attribute pair.
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApp4 {
class Program {
static void Main(string[] args) {
string inputString = "time, M1.A, M1.B, M1.C, M2.A, M2.B, M2.C, M3.A, M3.B, M3.C";
string[] attrList = inputString.Split(',');
// 1. Get all machines with attributes
List<MachineAttribute> MachineAttributeList = new List<MachineAttribute>();
for (int i = 1; i < attrList.Length; i++) {
MachineAttributeList.Add(new MachineAttribute(attrList[i]));
}
// 2. For each machine create
foreach (var machine in MachineAttributeList.Select(x=>x.Machine).Distinct()) {
Console.Write(attrList[0]);
foreach (var attribute in MachineAttributeList.Where(x=>x.Machine == machine)) {
Console.Write(attribute + ",");
}
Console.WriteLine();
}
Console.ReadLine();
}
}
public class MachineAttribute {
public string Machine { get; }
public string Attribute { get; }
public MachineAttribute(string inputData) {
var array = inputData.Split('.');
if (array.Length > 0) Machine = array[0];
if (array.Length > 1) Attribute = array[1];
}
public override string ToString() {
return Machine + "." + Attribute;
}
}
}
Okay So I have this Hashset that contains 3 items and I want to apply some logic on it such that I am able to append some predefined 3 values for the each item present inside the hashset
for example,
HashSet<string> hs = new HashSet<string>(); //it could be string or some other class object
hs.add("Red");
hs.add("Yellow");
hs.add("Blue");
//Some predefined values for those strings that I want to append to them
string[] str = {Alpha, Beta, Gamma}
The output I desire is:
unique strings associating "RedAlpha", "YellowBeta", "bluegamma"
for example s1 = "RedAlpha", s2 = "YellowBeta", s3 = "bluegamma";
I then want to apply some different logic to each of them later but then I guess that is a different thing
My Tried code
int count = 1;
int index = 0;
string s = "";
foreach(string strr in hs)
{
string s + count = strr + str[index]; // I don't know how to make new unique string
count++;
index++;
}
My other Tried Code,
foreach(string strr in hs)
{
string s = strr + str[index];
s = s + ","
index++;
}
s.split(",");
When you want to merge 2 collection together and perform some operation on them, use the Zip method. See this answer for an explanation of Zip method.
Here is how to achieve what you need:
HashSet<string> hs = new HashSet<string>();
hs.Add("Red");
hs.Add("Yellow");
hs.Add("Blue");
string[] str = { "Alpha", "Beta", "Gamma" };
List<KeyValuePair<string, string>> kvps =
hs.Zip(str, (left, right) => new KeyValuePair<string, string>(left, right))
.ToList();
If you want a dictionary, it is straight forward as well:
Dictionary<string, string> kvps =
hs.Zip(str, (left, right) => new { left, right })
.ToDictionary(x => x.left, x.right);
Put them in a list:
int index = 0;
var list = new List<string>();
foreach(string strr in hs)
{
list.Add(strr + str[index]);
index++;
}
Console.WriteLine(list[0]); //RedAlpha
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();
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;
}