0

I need to pull a bunch of key value pairs based on a predefined pattern from a string. An example of what I would need is this:

Pattern: {value1}-{value2}

String: Example-String

Result KVP:

{ Key: value1, value: Example }, { Key: value2, value: String } 

The catch is that the pattern could be pretty much anything (although the values I'd need to extract would always be surrounded in curly brackets), ie:

Pattern: {test1}\{test2}={value}

String: Example\Value=Result

Result KVP:

{ Key: test1, value: Example }, { Key: test2, value: Value }, { Key: value, value: Result } 

What I have done so far isn't quite working and I'm quite certain that there has to be a more elegant way of doing this as opposed to my solution anyway so I thought I'd see if anyone here would have a good idea.

EDIT:

Here is essentially what I have so far (it's working, but IMO it's really ugly):

public List<KeyValuePair<string, string>> Example(string pattern, string input) { var values = new List<KeyValuePair<string, string>>(); var r1 = Regex.Matches(input, @"(\{[A-Z,a-z]*\})"); string newregex = string.Empty; foreach (Match item in r1) { newregex = newregex.Replace(item.Value, "(.*?)"); //updates regex so that it adds this as a group for use later, ie: "{item1}-{item2}" will become "(.*?)-{item2}" string field = item.Value.Substring(1, item.Value.Length - 2); // {test1} will return "test1" values.Add(new KeyValuePair<string, string>(field, string.Empty)); } newregex = $"{newregex}\\z"; // ensures that it matches to end of input var r2 = Regex.Match(input, newregex); // KVP index (used below) int val = 0; foreach (Group g in r2.Groups) { if (g.Value == input) continue; // first group will be equal to input, ignore values[val] = new KeyValuePair<string, string>(values[val].Key, g.Value); // update KVP at index with new KVP with the value val++; } return values; } 
6
  • Sounds like you need some regex Commented Nov 18, 2019 at 19:37
  • Do you have a predefined list of delimeters (like '-' and '='), or at least some rule that defines a delimeter (i.e. any non-alphanumeric character)? Commented Nov 18, 2019 at 19:37
  • @RufusL currently we're only using '-' and '_'. I was hoping to come up with a general solution in case in future we have a need for different delimiters though. Commented Nov 18, 2019 at 19:40
  • Are you looking for a method that takes in two strings (a pattern and a search string) and returns a List<KeyValuePair>? It would be extremely helpful to see what you have done so far so we can see where it's not working. Commented Nov 18, 2019 at 19:41
  • @RufusL essentially, yes, a method like that would be ideal. I'll edit my post with what I have done shortly. Commented Nov 18, 2019 at 19:42

1 Answer 1

1

Unfortunately I don't know regular expressions very well, but one way to solve this is to walk through each character of the pattern string and create a list of keys and delimeters, after which we can walk through the search string, and find the index of each delimeter to get the current value, and then add a new KeyValuePair to a list.

Here's a rough sample that assumes good input:

public static List<KeyValuePair<string, string>> GetKVPs(string pattern, string search) { var results = new List<KeyValuePair<string, string>>(); var keys = new List<string>(); var delimeters = new List<string>(); var currentKey = string.Empty; var currentDelimeter = string.Empty; var processingKey = false; // Populate our lists of Keys and Delimeters foreach (var chr in pattern) { switch (chr) { case '}': { if (currentKey.Length > 0) { keys.Add(currentKey); currentKey = string.Empty; } processingKey = false; break; } case '{': { if (currentDelimeter.Length > 0) { delimeters.Add(currentDelimeter); currentDelimeter = string.Empty; } processingKey = true; break; } default: { if (processingKey) { currentKey += chr; } else { currentDelimeter += chr; } break; } } } if (currentDelimeter.Length > 0) delimeters.Add(currentDelimeter); var lastDelim = -1; // Find our Values based on the delimeter positions in the search string for (int i = 0; i < delimeters.Count; i++) { var delimIndex = search.IndexOf(delimeters[i], lastDelim + 1); if (delimIndex > -1) { var value = search.Substring(lastDelim + 1, delimIndex - lastDelim - 1); results.Add(new KeyValuePair<string, string>(keys[i], value)); lastDelim = delimIndex + delimeters[i].Length - 1; } } // Add the item after the final delimeter if it exists: if (lastDelim > -1 && lastDelim < search.Length - 1) { results.Add(new KeyValuePair<string, string>(keys.Last(), search.Substring(lastDelim + 1))); } return results; } 

And an example of it in action:

public static void Main(string[] args) { var results = GetKVPs( "{greeting}, {recipient}, this is {sender}.", "Hello, Dolly, this is Louis."); foreach (var kvp in results) { Console.WriteLine($"{kvp.Key} = {kvp.Value}"); } GetKeyFromUser("\nDone! Press any key to exit..."); } 

Output

enter image description here

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.