0

I want to replace values of a string with a dictionary that I have but only if they comply a regex condition.

This is what I have:

string input = @"A.4 AND ([10] A.4 OR A.4) OR [10]A.4 A.5 [10]A.5";
Dictionary <string, string> dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) {
                                                    {"A.4", "Test"},
                                                    {"A.5", "Test2"},
                                                    };
var output = dict.Aggregate(input, (current, value) => current.Replace(value.Key, value.Value));

//current output = "Test AND ([10] Test OR Test) OR [10]Test Test2 [10]Test2"
//wished output = "Test AND ([10] A.4 OR Test) OR [10]A.4 Test2 [10]A.5"

I don't want to replace the text when there is "[10]" or "[10] " in front of the text. I think that I should use a regex or something similar but I don't know how.

2
  • You already mentioned a Regex. Have you tried using Regex.Replace instead of this? Or at least write a Regex that matches the parts you want? Commented Oct 5, 2022 at 9:34
  • What do the tags look like? If they have a pattern you could use it in a single regex, eg (?<!\[10\]\s?)A\.\d+ would match all A.n values without the prefix Commented Oct 5, 2022 at 9:38

2 Answers 2

2

You could use regex to perform the replace operation:

var output = dict.Aggregate(input, (current, sub) => Regex.Replace(current, $@"(?<!\[10\]\s?){Regex.Escape(sub.Key)}", sub.Value));

The negative lookbehind assertion (?<!\[10\]\s?) will ensure the matched term never follows [10] or [10] .


As Panagiotis Kanavos notes you can skip the Aggregate call completely by passing a delegate that performs a dictionary lookup to Regex.Replace:

var replacePattern = $@"(?<!\[10\]\s?)(?:{string.Join('|', dict.Keys.Select(Regex.Escape))})";
var output = Regex.Replace(input, replacePattern, (m) =>  dict[m.Value]);
Sign up to request clarification or add additional context in comments.

4 Comments

There's no need for Aggregate, at least for replacement. Regex.Replace has an overload with a delegate that returns the replacement value.
@PanagiotisKanavos Good point, added to answer
@Mathias R. Jessen Thank you so much, that totally worth for me. However I will use the solution with Aggregate because I have keys like (A.4 and A.41) and with the second solution the A.41 is not replaced with the correct value, it's replace with the A.4 value and then there is a 1 at the end. So I'm using the first one with dict.Reverse().Aggregate(... being dict a SortedDictionary so the longest keys will be checked first.
@EvilMorty add \b after the last ) in replacePattern to fix that :)
1

Regex.Replace has an overload with a delegate that creates replacement values. If the tags have a pattern, a single regular expression can be used to match them and replace them using the dictionary values.

This regex matches tags in the form A.n where n a number:

var regex=new Regex(@"(?<!\[10\]\s?)A\.\d+");

var output=regex.Replace(input,match=>dict[match.Value]);

Console.WriteLine(output);

This produces

Test AND ([10] A.4 OR Test) OR [10]A.4 Test2 [10]A.5

(?<!...) is a negative lookbehind pattern. It matches strings that don't start with the negated pattern. (?<!\[10\]\s?) matches strings that don't start with [10] and an optional space.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.