0

First of all, I'd like to point out that the desired result is a char, not its position index (int).
I'm trying to give my users an option of choosing their desired date format and as such, I've created 2 comboBoxes: comboBox_DateFormatDivider where user chooses between dot, dash and a slash; and comboBox_DateFormat where user chooses date format. comboBox_DateFormat contains a List<string> as follows:

_dateFormatsList = new List<string>()
{
    "d-M-yy (" + DateTime.Now.ToString("d-M-yy") + ")",
    "dd-M-yy (" + DateTime.Now.ToString("dd-M-yy") + ")",
    "d-MM-yy (" + DateTime.Now.ToString("d-MM-yy") + ")",
    "dd-MM-yy (" + DateTime.Now.ToString("dd-MM-yy") + ")",
    "d-M-yyyy (" + DateTime.Now.ToString("d-M-yyyy") + ")",
    "dd-M-yyyy (" + DateTime.Now.ToString("dd-M-yyyy") + ")",
    "d-MM-yyyy (" + DateTime.Now.ToString("d-MM-yyyy") + ")",
    "dd-MM-yyyy (" + DateTime.Now.ToString("dd-MM-yyyy") + ")",
    "yy-M-d (" + DateTime.Now.ToString("yy-M-d") + ")",
    "yy-M-dd (" + DateTime.Now.ToString("yy-M-dd") + ")",
    "yy-MM-d (" + DateTime.Now.ToString("yy-MM-d") + ")",
    "yy-MM-dd (" + DateTime.Now.ToString("yy-MM-dd") + ")",
    "yyyy-M-d (" + DateTime.Now.ToString("yyyy-M-d") + ")",
    "yyyy-M-dd (" + DateTime.Now.ToString("yyyy-M-dd") + ")",
    "yyyy-MM-d (" + DateTime.Now.ToString("yyyy-MM-d") + ")",
    "yyyy-MM-dd (" + DateTime.Now.ToString("yyyy-MM-dd") + ")"
};
comboBox_DateFormat.DataSource = _dateFormatsList;

When user chooses different divider, it has to be reflected in the other comboBox, as such DateFormat is dependent on DateFormatDivider, so its contents have to be changed at runtime. And here's the code (and the question) for that:

private void comboBox_DateFormatDivider_SelectedIndexChanged(object sender, EventArgs e)
{
    for (int i = 0; i < _dateFormatsList.Count; i++)
    {
        _dateFormatsList[i] = _dateFormatsList[i].Replace(_dateFormatsList[i].???.ToString(), comboBox_DateFormatDivider.SelectedText);
    }
}

Chosen date format is later saved to database, so I guess I could also add another field where divider would be stored, but I'd prefer not to. As you can see, the code above contains ??? in it. So, the question is: how do I replace these question marks with code that will find me the divider it contains?

user6807975
  • 125
  • 3
  • 12
  • 2
    You should do `DateTime now = DateTime.Now;` before you initialize that list to avoid a case when the value of `DateTime.Now` changes inbetween elements. – Abion47 Jan 13 '17 at 11:01
  • Do Regex.Match to find the relevant divider and add the logic, wouldn't that be simple, use something like `Regex.Match(Value,".*(?:-).*")` and that would give you the divider to replace – Mrinal Kamboj Jan 13 '17 at 11:02
  • 1
    You could also just store your format string as something like `"dd@mm@yyyy"`, then when you use it for a `DateTime.ToString`, replace the @ characters with the chosen separator using `_dateFormatsList[i].Replace("@", _chosenSeparator)`. – Abion47 Jan 13 '17 at 11:05
  • Please, please, please don't go to Regex to solve this problem! Abion47 has given you a good answer. – swatsonpicken Jan 13 '17 at 11:08
  • @swatsonpicken And what's wrong with Regex ? – Mrinal Kamboj Jan 13 '17 at 11:08
  • @swatsonpicken Abion* – Abion47 Jan 13 '17 at 11:10
  • @MrinalKamboj It's an unnecessarily complicated solution to the problem. – swatsonpicken Jan 13 '17 at 11:11
  • @Abion47 thanks for the `DateTime` tip, wise call; however, the moethod with `@` would require to save the divider to db and then read it every time, and as I said, I'd prefer not having to save it. – user6807975 Jan 13 '17 at 11:12
  • @MrinalKamboj There's nothing wrong with it in general, it's just that there's many times when Regex is abused to solve an apparent complex problem and, in doing so, obscures the actual problem. I would wager that this is one of those cases. You _could_ use Regex to solve the task of finding whether a character exists in a string, but that approach as a whole is unnecessary in this application and serves only to needlessly complicate things. – Abion47 Jan 13 '17 at 11:15
  • @user6807975 Out of curiosity, what _are_ you saving to the database? (Give an example.) – Abion47 Jan 13 '17 at 11:16
  • @swatsonpicken @MrinalKamboj I actually thought about using regex (since I don't find them that confusing), but I'm not skillful enough in that field (e.g. I don't really understand what `(?:-)` does) to come up with the right expression and I'd most likely have to ask here if my regex is correct anyway. – user6807975 Jan 13 '17 at 11:16
  • If you say Regex is complex, then I rest my case, one of most widely used stuff for any kind of string search, all kinds of tools including Find and Grep use it effectively internally. Its just about understanding and using it well. – Mrinal Kamboj Jan 13 '17 at 11:18
  • @Abion47 I save the date format exactly as user chose it minus the brackets showing example, e.g. db gets exactly "dd-MM-yy" or "d/MM/yyyy" – user6807975 Jan 13 '17 at 11:18
  • Then why would you need to save the current separator to the database either way? It seems like the separator would be saved as part of the format. The only time you would need to "store" the current separator would be in the program itself, just as a utility variable. – Abion47 Jan 13 '17 at 11:20
  • @MrinalKamboj I never said it was complex. I said it was _unnecessarily_ complicated for the problem at hand. There is a distinction. – swatsonpicken Jan 13 '17 at 11:27
  • In your example, you use `_chosenSeparator` - in order to use this variable, I'd have to get this value from somewhere (either extract from date format or get it straight from db), since I have to not only initialize `comboBox_DateFormatDivider` values, but also set the value user saved previously, thus - I'd have to save it, and I'd prefer not to.
    Also, if I save it with `@`, then if someone else wants to use it anywhere else in their part of code, they'd have to do the exact same operation of getting the divider and replacing the `@`, which makes it more complicated than it should be.
    – user6807975 Jan 13 '17 at 11:28
  • @user6807975 I used `_chosenSeparator` as a stand-in variable since I did not know what you are using to store the user's chosen divider. Looking at your current code, however, it seems like you use `comboBox_DateFormatDivider.SelectedText` as your source, so just use that. – Abion47 Jan 13 '17 at 11:31

3 Answers3

2

During those rare cases where you absolutely must do string searching, you can just do the following:

string[] separators = new string[] { "-", ".", "/" };

void DetectCurrentSeparator(string dbDateFormat)
{
    for (int i = 0; i < separators.Length; i++)
    {
        if (dbDateFormat.IndexOf(separators[i]) >= 0)
        {
            // Assuming the above array corresponds with the order of the divider combobox
            combobox_DateFormatDivider.SelectedIndex = i;
            break;
        }
    }
}

Now this having been said, this method should only need to ever be used once during the life of your program - when the date format is first retrieved from the database. For the rest of the time, there is no reason to use any string searching at all for this. You already have the desired divider from your combo box, so instead of trying to figure out what the existing divider is, use a common temporary character and just replace that.

Initialize like so:

DateTime now = DateTime.Now;
_dateFormatsMasterList = new List<string>()
{
    "d@M@yy (" + now.ToString("d@M@yy") + ")",
    "dd@M@yy (" + now.ToString("dd@M@yy") + ")",
    "d@MM@yy (" + now.ToString("d@MM@yy") + ")",
    "dd@MM@yy (" + now.ToString("dd@MM@yy") + ")",
    "d@M@yyyy (" + now.ToString("d@M@yyyy") + ")",
    "dd@M@yyyy (" + now.ToString("dd@M@yyyy") + ")",
    "d@MM@yyyy (" + now.ToString("d@MM@yyyy") + ")",
    "dd@MM@yyyy (" + now.ToString("dd@MM@yyyy") + ")",
    "yy@M@d (" + now.ToString("yy@M@d") + ")",
    "yy@M@dd (" + now.ToString("yy@M@dd") + ")",
    "yy@MM@d (" + now.ToString("yy@MM@d") + ")",
    "yy@MM@dd (" + now.ToString("yy@MM@dd") + ")",
    "yyyy@M@d (" + now.ToString("yyyy@M@d") + ")",
    "yyyy@M@dd (" + now.ToString("yyyy@M@dd") + ")",
    "yyyy@MM@d (" + now.ToString("yyyy@MM@d") + ")",
    "yyyy@MM@dd (" + now.ToString("yyyy@MM@dd") + ")"
};

_dateFormatsList = new List<string>(_dateFormatsMasterList);
comboBox_DateFormat.DataSource = _dateFormatsList;

And your SelectionChanged event like so:

private void comboBox_DateFormatDivider_SelectedIndexChanged(object sender, EventArgs e)
{
    for (int i = 0; i < _dateFormatsList.Count; i++)
    {
        _dateFormatsList[i] = _dateFormatsMasterList[i].Replace("@", comboBox_DateFormatDivider.SelectedText);
    }
}
Abion47
  • 22,211
  • 4
  • 65
  • 88
  • As mentioned in comments under the question itself: this requires me to save the divider into db, which I said I'd prefer not to. Why this requires that? Because in order to use `comboBox_DateFormatDivider.SelectedText`I have to first set this with value user has already chosen and saved before; and how can I get this value if I don't do string searching or if I don't save it to db? Not to mention, if I use `@`, then I cannot even do string searching (it'd be pointless) – user6807975 Jan 13 '17 at 11:38
  • The chosen divider is in the date format you **are** saving to the db. When you come to display what the user has saved before you can extract the divider from the saved format (using string searching) – swatsonpicken Jan 13 '17 at 11:42
  • @user6807975 Dude, when you save a format to the db, it is in the format of "dd-mm-yyyy", "dd.mm.yyyy", or "dd/mm/yy" correct? The separator is _right there_ in the saved string. IF you absolutely must, you can pull the separator from that string, but that only needs to be done once ever - when that string is pulled from the db. During normal program use, there is absolutely no reason to use string searching for your operation. – Abion47 Jan 13 '17 at 11:45
  • Then I need to do string searching for that divider. And how do I do that? - that's the very question in this topic. And since I have to search the string for it anyway, why not just go with @MrinalKamboj answer, since it has this and everything else already, in one place? Your answer still requires everyone to do that process (of extracting divider) in every code snippet where someone wants to use this date format. If I go with no `@`s, people using this date format just get it from db and care for nothing else. – user6807975 Jan 13 '17 at 11:48
  • @Abion47 Sure, I agree, no point in doing excess work with multiple string searchings, but it still has to be done and that is the question here and your answer doesn't answer that question. – user6807975 Jan 13 '17 at 11:50
  • @user6807975 Well in that case, Regex is _still_ overcomplicating the issue. You can do the string search with a simple `IndexOf`. I'll edit the code in a sec. – Abion47 Jan 13 '17 at 11:52
  • @Abion47 and now this answer indeed asnwers my question, thank you. I'd suggest editing it so that these 2 'parts' are swapped so that the answer to the actual question is first. – user6807975 Jan 13 '17 at 12:39
  • @user6807975 Very well. – Abion47 Jan 13 '17 at 12:44
1

Assuming that number of dividers in the Date format are finite, I will do the following:

List<string> dividers = new List<string>{".","/","-"}

private void comboBox_DateFormatDivider_SelectedIndexChanged(object sender, EventArgs e)
{
    string currentSeparator = string.Empty;

     foreach(var s in dividers)
     {
     string pattern = @".*(?:\" + s + ").*";

        if(Regex.IsMatch(_dateFormatsList[0],pattern))
        {
          currentSeparator = s;
          break;    
         }    
     }
    for (int i = 0; i < _dateFormatsList.Count; i++)
    {
        _dateFormatsList[i] = _dateFormatsList[i].Replace(currentSeparator, comboBox_DateFormatDivider.SelectedText);
    }
}

Edit 1:

Explanation about regular expression:

Following are details: ".*(?:\.).*", .* means anything which means 0 or more occurrence, of any character, ?: refers to pattern search matching starts and it expects the character being passed (here "."), (which is escaped, to avoid special treatment of special characters like ., *, ? etc) to be searched in all the data an it will return true on first occurrence or else false.

Edit 2:

To provide an example of the potential of Regular expressions, in the _dateFormatsList, you have multiple date formats, but all the solutions, just pick one value to figure out operator, what about the other values, what if there's data corruption and more than one separators exist, following code will take care of lot of such issues and can further be made strict by changing the pattern:

string pattern = @"^(?!\.\/)[\d]{1,4}[\-][\d]{1,2}[\-][\d]{1,4}$";

It represent all the date formats which contains separator - but not . or /, also there's a digit (\d) repetition, before a separator -, this pattern needs to be applied using Linq All operator as follows:

bool result = _dateFormatsList.All(data => Regex.IsMatch(data,pattern));

This is just to depict, the power of Regular expressions, which no other solution can help achieve, I think most people find Regex complex due to lack of understanding regarding pattern creation

Mrinal Kamboj
  • 11,300
  • 5
  • 40
  • 74
  • Again, far more complicated than it needs to be. – Abion47 Jan 13 '17 at 11:18
  • Point out the Complexity, tell me where can I make it simpler – Mrinal Kamboj Jan 13 '17 at 11:18
  • The complexity is in using Regex at all. If the design were tweaked just slightly, it would eliminate the need to do string searching at all. See my answer. – Abion47 Jan 13 '17 at 11:29
  • I really like this solution, I could simplify the `foreach break` loop in my code and I could put a comment explaining what the regex does for devs which might not understand it. However, would you please explain this regex more thoroughly for me and for posterity's sake? – user6807975 Jan 13 '17 at 11:33
  • @Abion47, I am not sure what you might have against Regex and what complexity does it adds here, I would in fact suggest to look under the hood, how much widely its used in wide variety of systems, all document processors, which fetch data patterns use it effectively. Also suggest the change of design which would fail the current regex, at most regular expression would need an update, which is simple as per use case – Mrinal Kamboj Jan 13 '17 at 11:59
  • @user6807975 Regex is a pattern matching technology, which is used by all programming paradigm to search an occurrence of a pattern in a given string or data, thus providing a quick search ability. If we learn the art of creating correct regular expressions, we can do wonders in the data analytics, I will try to find some easy links for your reference – Mrinal Kamboj Jan 13 '17 at 12:01
  • 1
    @MrinalKamboj Regex is not necessary because pattern matching is not necessary. A simple `IndexOf` will suffice just as well, and it is both faster and simpler. It is a matter of using the right tool for the job, and in this case using Regex is like using a sledgehammer on a thumbtack. – Abion47 Jan 13 '17 at 12:02
  • @MrinalKamboj I don't doubt that Regex is used in a vast assortment of applications. I myself use it extensively for various purposes. But with that use also comes the ability to recognize when I have to pull out Regex or when something simpler or more appropriate will do the job just as well. The insistence on whipping out Regex for everything even remotely string-search related is why we end up with [cases where demon spawn threaten to overtake the world.](http://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags/1732454#1732454) – Abion47 Jan 13 '17 at 12:07
  • @MrinalKamboj I didn't mean 'to explain what regex is' but 'to explain this particular regex expression, symbol by symbol, what it is there for and what if returns'. Also, I've just found out your code doesn't work - it always returns first element from `dividers` list, i.e. regex matches returns success even then the divider is "/" – user6807975 Jan 13 '17 at 12:10
  • @Abion47, Indexof does the linear search, when pattern matching using regex is not linear, for such a small dataset, both will be extremely fast. Nonetheless, I still don't get what is complicated about the regex expression used here, its your prerogative to not use it. – Mrinal Kamboj Jan 13 '17 at 12:20
  • @user6807975 The fact that a symbol by symbol explanation of the regex is required should ring alarm bells, if not for you then for other, possibly less experienced, developers that may have to maintain your code in the future. Abion47 has given you a simple, elegant solution. – swatsonpicken Jan 13 '17 at 12:21
  • @MrinalKamboj The fact that your current Regex solution doesn't work is a red flag right there. The problem is that the first separator is `"."`, which when plugged directly into your pattern matches _any_ character. To fix it, you would need to change your string for that separator to be `"\\."`, but that is a specific fix just to make this solution work. Hence, unnecessary complexity. – Abion47 Jan 13 '17 at 12:29
  • @swatsonpicken I agree, it does ring alarm bells, reason why I asked for explanation is for other users which may stumble upon this question in future and would like to know/understand what this regex does. – user6807975 Jan 13 '17 at 12:30
  • @user6807975 Please check the edit, I have modified the regular expression to escape the character for correct working \, since currently they are treated specially, For all the alarm bells, I have no need to defend regular expressions, they have a huge use case for them, I will explain the expression again in the answer edit – Mrinal Kamboj Jan 13 '17 at 12:43
  • @MrinalKamboj Again, Regex has plenty of use cases - this is just not one of them. Take your pattern for example. First, you bound the separator in a group. Then you check around the group for other characters. Both of these are unnecessary patterns, as the goal is **merely to check for the existence of the separator character at all**. The Regex approach can be simplified down to the pattern just being the character you are searching for and then verifying that the result isn't a failed match... which would be logically identical to the `IndexOf` method using more complicated means. – Abion47 Jan 13 '17 at 12:50
  • @Abion47, Escaping is not a specific solution, its the general practice, which I missed out in first solution. Simple Regex would be "\." in this case, but idea is to comprehensively provide a Regular expression, based on standard practice not a case specific solution. Nonetheless, I rest my case, please don't use Regex ever, this is the most complex feature. – Mrinal Kamboj Jan 13 '17 at 13:04
  • @user6807975, check out Edit 2 to understand the power of Regular expressions, this is one simple example, of how lot of use cases can be covered by a simple pattern – Mrinal Kamboj Jan 13 '17 at 18:26
0

first of all, i'd like to go with Abion47's advice to use a variable to store date and use it in the list.

and then you can do this

char x = _dateFormatsList[0][1];
        for (int i = 0; i < _dateFormatsList.Count; i++)
            {
                _dateFormatsList[i] = _dateFormatsList[i].Replace(x, comboBox_DateFormatDivider.SelectedItem.ToString()[0]);
            }
  • yes. it is a list of string and Replace() works with char, so got it directly from the list. – hitesh sharma Jan 13 '17 at 12:15
  • 1
    If the data source of the combo box gets sorted then this solution fails as the value of x will be the character "d". Better to use something like the DetectCurrentSeparator method from Abion47's solution. – swatsonpicken Jan 13 '17 at 12:58