Plurality in user messages - c#

Many times, when generating messages to show to the user, the message will contain a number of something that I want to inform the customer about.
I'll give an example: The customer has selected a number of items from 1 and up, and has clicked delete. Now I want to give a confirmation message to the customer, and I want to mention the number of items he has selected to minimize the chance of him making a mistake by selecting a bunch of items and clicking delete when he only wants to delete one of them.
One way is to make the generic message like this:
int noofitemsselected = SomeFunction();
string message = "You have selected " + noofitemsselected + " item(s). Are you sure you want to delete it/them?";
The "problem" here is the case where noofitemselected is 1, and we have to write item and it instead of items and them.
My normal solution will be something like this
int noofitemsselected = SomeFunction();
string message = "You have selected " + noofitemsselected + " " + (noofitemsselected==1?"item" : "items") + ". Are you sure you want to delete " + (noofitemsselected==1?"it" : "them") + "?";
This gets quite long and quite nasty really fast if there are many references to the numbers plurality inside the code, and the actual message gets hard to read.
So my questions is simply. Are there any better ways of generating messages like this?
EDIT
I see a lot of persons has got very hung up in the case that I mentioned that the message should be displayed inside a message box, and has simply given an answer of how to avoid using the message box at all, and that is all good.
But remember that the problem of pluralization also apply to texts other places in the program in addition to message boxes. For example, a label alongside a grid displaying the number of lines selected in the grid will have the same problem regarding pluralization.
So this basically apply to most text that is outputted in some way from programs, and then the solution is not as simple as to just change the program to not output text anymore :)

You can avoid all of this messy plurality by just deleting the items without any message and giving the user a really good Undo facility. Users never read anything. You should build a good Undo facility as part of your program anyway.
You actually get 2 benefits when you createe a comprehensive Undo facility. The first benefit makes the user's life easier by allowing him/her to reverse mistakes and minimise reading. The second benefit is that your app is reflecting real life by allowing the reversal of non-trivial workflow (not just mistakes).
I once wrote an app without using a single dialog or confirmation message. It took some serious thinking and was significantly harder to implement than using confirmation-type messages. But the end result was rather nice to use according to its end-users.

If there is ever any chance, no matter how small, that this app will need to be translated to other languages then both are wrong. The correct way of doing this is:
string message = ( noofitemsselected==1 ?
"You have selected " + noofitemsselected + " item. Are you sure you want to delete it?":
"You have selected " + noofitemsselected + " items. Are you sure you want to delete them?"
);
This is because different languages handle plurality differently. Some like Malay don't even have syntactic plurals so the strings would generally be identical. Separating the two strings makes it easier to support other languages later on.
Otherwise if this app is meant to be consumed by the general public and is supposed to be user friendly then the second method is preferable. Sorry but I don't really know a shorter way of doing this.
If this app is meant to be consumed only internally by your company then do the shortcut "item(s)" thing. You don't really have to impress anybody when writing enterprisy code. But I'd advise against doing this for publicly consumed app because this gives the impression that the programmer is lazy and thus lower their opinion of the quality of the app. Trust me, small things like this matter.

How about just:
string message = "Are you sure you want to delete " + noofitemsselected + " item(s)?"
That way, you eliminate the number agreement difficulties, and end up with an even shorter, more to-the-point error message for the user as a bonus. We all know users don't read error messages anyway. The shorter they are, the more likely they are to at least glance at the text.
Or, armed with this knowledge that users don't read error messages, you could approach this a different way. Skip the confirmation message altogether, and just provide an undo feature that Just Works, regardless of what was deleted. Most users are already accustomed to undoing an operation when they notice it was not what they wanted, and are likely to find this behavior more natural than having to deal with another annoying pop-up.

What about what Java has had for years: java.text.MessageFormat and ChoiceFormat? See http://download.oracle.com/javase/1.4.2/docs/api/java/text/MessageFormat.html for more information.
MessageFormat form = new MessageFormat("The disk \"{1}\" contains {0}.");
form.applyPattern(
"There {0,choice,0#are no files|1#is one file|1<are {0,number,integer} files}.");
Object[] testArgs = {new Long(12373), "MyDisk"};
System.out.println(form.format(testArgs));
// output, with different testArgs
output: The disk "MyDisk" are no files.
output: The disk "MyDisk" is one file.
output: The disk "MyDisk" are 1,273 files.
In your case you want something somewhat simpler:
MessageFormat form = new MessageFormat("Are you sure you want to delete {0,choice,1#one item,1<{0,number.integer} files}?");
The advantage of this approach is that it works well with the i18n bundles, and you can provide translations properly for languages (like Japanese) that have no concept of plural or singular words.

I'd go with not hardcoding the message, but providing two messages in an seperate Resource file. Like
string DELETE_SINGLE = "You have selected {0} item. Are you sure you want to delete it?";
string DELETE_MULTI = "You have selected {0} items. Are you sure you want to delete them?";
and then feeding them into String.Format like
if(noofitemsselected == 1)
messageTemplate = MessageResources.DELETE_SINGLE;
else
messageTemplate = MessageResources.DELETE_MULTI;
string message = String.Format(messageTemplate, noofitemsselected)
I think that this approach is easier to localize and maintain. All UI messages would be at a single locaion.

You can sidestep the issue entirely by phrasing the message differently.
string message = "The number of selected items is " + noofitemsselected + ". Are you sure you want to delete everything in this selection?";

The first thing I'd suggest is: use string.Format. That allows you to do something like this:
int numOfItems = GetNumOfItems();
string msgTemplate;
msgTemplate = numOfItems == 1 ? "You selected only {0} item." : "Wow, you selected {0} items!";
string msg = string.Format(msgTemplate, numOfItems);
Further, in WPF apps, I've seen systems where a resource string would be pipe-delimited to have two messages: a singular and a plural message (or a zero/single/many message, even). A custom converter could then be used to parse this resource and use the relevant (formatted) string, so your Xaml is something like this:
<TextBlock Text="{Binding numOfItems, Converter={StaticResource c:NumericMessageFormatter}, ConverterParameter={StaticResource s:SuitableMessageTemplate}}" />

For English, plenty of answers above. For other languages it is more difficult, as plurals depend on the gender of the noun and the word ending. Some examples in French:
Regular masculine:
Vous avez choisi 1 compte. Voulez-vous vraiment le supprimer.
Vous avez choisi 2 comptes. Voulez-vous vraiment les supprimer.
Regular feminine
Vous avez choisi 1 table. Voulez-vous vraiment la supprimer.
Vous avez choisi 2 tables. Voulez-vous vraiment les supprimer.
Irregular masculine (finishes with 's')
Vous avez choisi 1 pays. Voulez-vous vraiment le supprimer.
Vous avez choisi 2 pays. Voulez-vous vraiment les supprimer?
The same problem exists in most Latin languages and gets worse in German or Russian, where there are 3 genders (maculine, feminine and neuter).
You'll need to take care if your objective is to handle more than just English.

To be able to have pluralized messages which will be possible to localize properly, my opinion is that it would be wise to first create a layer of indirection between the number and a message.
For example, use a constant of some sort to specify which message you want to display. Fetch the message using some function that will hide the implementation details.
get_message(DELETE_WARNING, quantity)
Next, create a dictionary that holds the possible messages and variations, and make variations know when they should be used.
DELETE_WARNING = {
1: 'Are you sure you want to delete %s item',
>1: 'Are you sure you want to delete %s items'
>5: 'My language has special plural above five, do you wish to delete it?'
}
Now you could simply find the key that corresponds to the quantity and interpolate the value of the quantity with that message.
This oversimplified and naive example, but I don't really see any other sane way to do this and be able to provide good support for L10N and I18N.

You'll have to translate the function below from VBA to C#, but your usage would change to:
int noofitemsselected = SomeFunction();
string message = Pluralize("You have selected # item[s]. Are you sure you want to delete [it/them]?", noofitemsselected);
I have a VBA function that I use in MS Access to do exactly what you are talking about. I know I'll get hacked to pieces for posting VBA, but here goes anyway. The algorithm should be apparent from the comments:
'---------------------------------------------------------------------------------------'
' Procedure : Pluralize'
' Purpose : Formats an English phrase to make verbs agree in number.'
' Usage : Msg = "There [is/are] # record[s]. [It/They] consist[s/] of # part[y/ies] each."'
' Pluralize(Msg, 1) --> "There is 1 record. It consists of 1 party each."'
' Pluralize(Msg, 6) --> "There are 6 records. They consist of 6 parties each."'
'---------------------------------------------------------------------------------------'
''
Function Pluralize(Text As String, Num As Variant, Optional NumToken As String = "#")
Const OpeningBracket = "\["
Const ClosingBracket = "\]"
Const DividingSlash = "/"
Const CharGroup = "([^\]]*)" 'Group of 0 or more characters not equal to closing bracket'
Dim IsPlural As Boolean, Msg As String, Pattern As String
On Error GoTo Err_Pluralize
If IsNumeric(Num) Then
IsPlural = (Num <> 1)
End If
Msg = Text
'Replace the number token with the actual number'
Msg = Replace(Msg, NumToken, Num)
'Replace [y/ies] style references'
Pattern = OpeningBracket & CharGroup & DividingSlash & CharGroup & ClosingBracket
Msg = RegExReplace(Pattern, Msg, "$" & IIf(IsPlural, 2, 1))
'Replace [s] style references'
Pattern = OpeningBracket & CharGroup & ClosingBracket
Msg = RegExReplace(Pattern, Msg, IIf(IsPlural, "$1", ""))
'Return the modified message'
Pluralize = Msg
End Function
Function RegExReplace(SearchPattern As String, _
TextToSearch As String, _
ReplacePattern As String) As String
Dim RE As Object
Set RE = CreateObject("vbscript.regexp")
With RE
.MultiLine = False
.Global = True
.IgnoreCase = False
.Pattern = SearchPattern
End With
RegExReplace = RE.Replace(TextToSearch, ReplacePattern)
End Function
The usage got cut off a bit in the code comments above, so I'll repeat it here:
Msg = "There [is/are] # record[s]. [It/They] consist[s/] of # part[y/ies] each."
Pluralize(Msg, 1) --> "There is 1 record. It consists of 1 party each."
Pluralize(Msg, 6) --> "There are 6 records. They consist of 6 parties each."
Yes, this solution ignores languages that are not English. Whether that matters depends on your requirements.

You could generate the plural automatically, see eg. plural generator.
For plural generating rules see wikipedia
string msg = "Do you want to delete " + numItems + GetPlural(" item", numItems) + "?";

How about a more generic way. Avoid pluralization in the second sentence:
Number of selected items to be deleted: noofitemsselected.
Are you sure?
I find out that doing it this way puts the number at the end of the line which is really easy to spot. This solution would work with the same logic in any language.

My general approach is to write a "single/plural function", like this:
public static string noun(int n, string single, string plural)
{
if (n==1)
return single;
else
return plural;
}
Then in the body of the message I call this function:
string message="Congratulations! You have won "+n+" "+noun(n, "foobar", "foobars")+"!";
This isn't a whole lot better, but at least it, (a) puts the decision in a function and so unclutters the code a little, and (b) is flexible enough to handle irregular plurals. i.e. it's easy enough to say noun(n, "child", "children") and the like.
Of course this only works for English, but the concept is readily extensible to languages with more complex endings.
It occurs to me that you could make the last parameter optional for the easy case:
public static string noun(int n, string single, string plural=null)
{
if (n==1)
return single;
else if (plural==null)
return single+"s";
else
return plural;
}

Internationalization
I assume you want internationalization support, in which case different languages have different patterns for plurals (e.g. a special plural form for 2 of something, or more complicated languages like Polish), and you can't rely on applying some simple pattern to your string to fix it.
You can use GNU Gettext's ngettext function and provide two English messages in your source code. Gettext will provide the infrastructure to choose from other (potentially more) messages when translated into other languages. See http://www.gnu.org/software/hello/manual/gettext/Plural-forms.html for a full description of GNU gettext's plural support.
GNU Gettext is under the LGPL. ngettext is named GettextResourceManager.GetPluralString in the C# port of Gettext.
(If you don't need localization support, and don't want to use Gettext right away, then write your own function that does this for English, and pass two full messages to it, that way if you need l10n later, you can add by rewriting a single function.)

How about to write function like
string GetOutputMessage(int count, string oneItemMsg, string multiItemMsg)
{
return string.Format("{0} {1}", count, count > 1 ? multiItemMsg : oneItemMsg);
}
.. and use it whenever you need?
string message = "You have selected " + GetOutputMessage(noofitemsselected,"item","items") + ". Are you sure you want to delete it/them?";

For the first problem , I mean Pluralize, you can use Inflector.
And for the second, you can use a string representation extension with a name such as ToPronounString.

I had this exact same question posed to me yesterday by a member of our team.
Since it came up again here on StackOverflow I figured the universe was telling me to have a bash at producing a decent solution.
I've quickly put something together and it's by no means perfect however it might be of use or spark some discussion/development.
This code is based on the idea that there can be 3 messages. One for zero items, one for one item and one for more than one item which follow the following structure:
singlePropertyName
singlePropertyName_Zero
singlePropertyName_Plural
I've created an internal class to test with in order to mimick the resource class. I haven't tested this using an actual resource file yet so I'm yet to see the full result.
Here's the code (currently i've included some generics where I know I could have specified the third param simply as a Type and also the second param is a string, I think there's a way to combine these two parameters into something better but I'll come back to that when I have a spare moment.
public static string GetMessage<T>(int count, string resourceSingularName, T resourceType) where T : Type
{
var resourcePluralName = resourceSingularName + "_Plural";
var resourceZeroName = resourceSingularName + "_Zero";
string resource = string.Empty;
if(count == 0)
{
resource = resourceZeroName;
}
else{
resource = (count <= 1)? resourceSingularName : resourcePluralName;
}
var x = resourceType.GetProperty(resource).GetValue(Activator.CreateInstance(resourceType),null);
return x.ToString();
}
Test resource class:
internal class TestMessenger
{
public string Tester{get{
return "Hello World of one";}}
public string Tester_Zero{get{
return "Hello no world";}}
public string Tester_Plural{get{
return "Hello Worlds";}}
}
and my quick executing method
void Main()
{
var message = GetMessage(56, "Tester",typeof(TestMessenger));
message.Dump();
}

From my point of view, your first solution is the most suited one. Why I say that is, in case you need the application to support multiple languages, the second option can be painstaking. With the fist approach it is easy to localize the text without much effort.

You could go for a more generic message like 'Are you sure you want to delete the selected item(s)'.

I depends on how nice a message you want to have. From easiest to hardest:
Re-write your error message to avoid pluralization. Not as nice for your user, but faster.
Use more general language but still include the number(s).
Use a "pluralization" and inflector system ala Rails, so you can say pluralize(5,'bunch') and get 5 bunches. Rails has a good pattern for this.
For internationalization, you need to look at what Java provides. That will support a wide variety of languages, including those that have different forms of adjectives with 2 or 3 items. The "s" solution is very English centric.
Which option you go with depends on your product goals. - ndp

Why would you want to present a message the users can actually understand? It goes against 40 years of programing history. Nooooo, we have a good thing going on, don't spoil it with understandable messages.
(j/k)

Do it like it's done in World of Warcraft:
BILLING_NAG_WARNING = "Your play time expires in %d |4minute:minutes;";

It gets a little bit shorter with
string message = "Are you sure you want to delete " + noofitemsselected + " item" + (noofitemsselected>1 ? "s" : "") + "?";

One approach I haven't seen mentioned would be the use of a substitution/select tag (e.g. something like "You are about to squash {0} [?i({0}=1):/cactus/cacti/]". (in other words, have a format-like expression specify the substitution based upon whether argument zero, taken as an integer, equals 1). I've seen such tags used in the days before .net; I'm not aware of any standard for them in .net, nor do I know the best way to format them.

I would think out of the box for a minute, all of the suggestions here are either do the pluralization (and worry about more than 1 level of pluralization, gender, etc) or not use it at all and provide a nice undo.
I would go the non lingual way and use visual queues for that. e.g. imagine an Iphone app you select items by wiping your finger. before deleting them using the master delete button, it will "shake" the selected items and show you a question mark titled box with a V (ok) or X (cancel) buttons...
Or, in the 3D world of Kinekt / Move / Wii - imagine selecting the files, moving your hand to the delete button and be told to move your hand above your head to confirm (using the same visual symbols as I mentioned before. e.g. instead of asking you delete 3 files? it will show you 3 files with a hovering half transparent red X on and tell you to do something to confirm.

Related

c# Data-Driven Approach [duplicate]

I've been tasked at work to write a detailed engineering plan for a logistics application that we are coding to propose to a customer. I have been told that it is a data-driven application. What does it mean for an application to be "data-driven"? What is the opposite? I can't seem to get any really clear answer for this although while web searching I can see many people posting their own examples. Any help would be greatly appreciated.
Data driven progamming is a programming model where the data itself controls the flow of the program and not the program logic. It is a model where you control the flow by offering different data sets to the program where the program logic is some generic form of flow or of state-changes.
For example if you have program that has four states: UP - DOWN - STOP - START
You can control this program by offering input (data) that represents the states:
set1: DOWN - STOP - START - STOP - UP - STOP
set2: UP - DOWN - UP - DOWN
The program code stays the same but data set (which is not of a dynamic input type but statically given to the computer) controls the flow.
Although there are more than a few ideas as to what data driven programming is, allow me to give an example using a data structure and a function.
Non data driven example:
data_lloyd = {'name': 'Lloyd', 'lives': 'Alcoy }
data_jason = {'name': 'Jason', 'lives': 'London' }
go = function(x)
if x.name == 'Lloyd'
then
print("Alcoy, Spain")
else
print("London, UK")
end
Data driven example:
data_lloyd = {'name': 'Lloyd', 'lives': function(){ print("Alcoy, Spain") }
data_jason = {'name': 'Jason', 'lives': function(){ print("London, UK") }
go = function(x)
x.lives()
end
In the first example the decision to show one result or the other is in the code logic.
In the last example the output is determined by the data that is passed to the function and for that reason we say the output is 'driven' by the data.
"I have been told that it is a data-driven application" - you need to ask whoever told you that.
You don't want to read some plausible answer here and then find out that it's not at all what the person in charge of your project meant. The phrase is too vague to have an unambiguous meaning that will definitely apply to your project.
Data driven development is something that one can make changes to the
logic of the program by editing not the code but the data structure.
You might find more information about data-driven programming here.
Procedural Programming
var data = {
{do:'add',arg:{1,2}},
{do:'subtract',arg:{3,2}},
{do:'multiply',arg:{5,7}},
};
foreach(var item in data){
switch(item.do){
case 'add':
console.log(item.arg[0] + item.arg[1]);
break;
case 'subtract':
console.log(item.arg[0] - item.arg[1]);
break;
case 'multiply':
console.log(item.arg[0] * item.arg[1]);
break;
}
}
Data Driven Programming
var data = {
{do:'+',arg:{1,2}},
{do:'-',arg:{3,2}},
{do:'*',arg:{5,7}},
};
foreach(var item in data){
console.log(eval (item.arg[0] + item.do + item.arg[1]);
}
Data driven application is:
(1) a set of rules accepting different data sets to make a predetermined decision for each specific data set and throwing outcome as result
(2) a few predetermined processes that are triggered based on the outcome.
Perfect example is ifttt.com
The application has nothing but rules.
What makes it useful is the data that will flow through it.
This article explains most clearly what I understand the term to mean:
What is Table-Driven and Data-Driven Programming?
http://www.paragoncorporation.com/ArticleDetail.aspx?ArticleID=31
Data/Table-Driven programming is the
technique of factoring repetitious
programming constructs into data and a
transformation pattern. This new data
is often referred to by purists as
meta-data when used in this fashion.
There is no one at work that can help you with this question? It is very hard to visualize what you are working without without a greater example. But from what I gather it is going to be a program that they primarily enter information into. That will be able to retrieve and edit information that the customer needs to manage.
Best of luck!!
I think the advice given isn't bad, but I've always thought of Data Driven Design revolves around using existing or given data structures as the foundation for your domain objects.
For instance, the classic salesperson management program might have the following type structure of tables:
Salesperson
Region
Customers
Products
So, your application would be centered around managing these data structures, instead of taking a straight API which does things like - "make sale" etc...
Just my opinion as the other answers suggest ;)
Imagine you need a program that prompts the user for nouns and adjectives (or other language constructs) which you will use to fill in a sentence (e.g. MadLibs).
Procedural example
noun1 = input('Noun: ')
noun2 = input('Noun: ')
adj = input('Adjective: ')
print(f'The {noun1} jumped over the {adj} {noun2}')
If you wanted to write a different version (more nouns, different phrase, etc.) you would write a different program.
Data-driven example
def get_inputs(inputs_needed):
inputs = {}
for key, prompt in inputs_needed.items():
inputs[key] = input(prompt + ': ')
return inputs
for game in games_json:
inputs = get_inputs(game['inputs_needed'])
print(game['phrase'].format(**inputs)
Now an individual game can be defined as:
{
"inputs_needed": {
"noun1": "Noun",
"noun2": "Noun",
"adj": "Adjective"
},
"phrase": "The {noun1} jumped over the {adj} {noun2}"
}
Now to create a new version, you simply change the JSON. The code stays the same.

C# Console Application where user enters resource and is told what Junk is required

Hoping someone can help. I'm a very basic beginner in C#. I set myself the task of creating a console application where I enter a resource I need i.e Screw or screws into the console, hit enter and it retrieves all the junk from the list that contain that word, along with the amount of resources you would receive from the junk. So Type Writer would be 2 screws. It's for Fallout 76 so I can see what junk contains what resource.
However I'm having a few issues:
The console only returns one result for screw, even though there are multiple results. (How do I resolve this and receive multiple results? i.e Clip board and Toy Car)
How do I search and get results for partial matches i.e if I type scre or screws?
Is there a better way of creating a console application where you store values and a user searches for those values? I cannot find anything close to what I need scouring the internet.
Thank you for all the help.
using System;
using System.Collections.Generic;
class JunkList
{
public string Resource { get; set; }
public string Junk { get; set; }
public int Amount { get; set; }
public JunkList(string r, string j, int a)
{
this.Resource = r;
this.Junk = j;
this.Amount = a;
}
}
class Program
{
static void Main(string[] args)
{
string searchName;
List<JunkList> infoList = new List<JunkList>();
infoList.Add(new JunkList("Screw", "Type Writer", 2));
infoList.Add(new JunkList("Screw", "Clip Board", 1));
infoList.Add(new JunkList("Screw", "Toy Car", 3));
Console.Write("Which resource do you want to search for?? \n");
searchName = Console.ReadLine();
for (int i = 0; i < infoList.Count; i++)
{
if (string.Compare(searchName, infoList[i].Resource, true) == 0)
{
Console.Write("Resource : " + infoList[i].Resource + "\n");
Console.Write("Junk : " + infoList[i].Junk + "\n");
Console.Write("Resource Amount : " + infoList[i].Amount + "\n");
break;
}
}
Console.ReadKey();
}
}
The console only returns one result for screw, even though there are
multiple results. (How do I resolve this and receive multiple results?
i.e Clip board and Toy Car)
Your issue is that you're breaking after finding the very first result:
if (string.Compare(searchName, infoList[i].Resource, true) == 0)
{
Console.Write("Resource : " + infoList[i].Resource + "\n");
Console.Write("Junk : " + infoList[i].Junk + "\n");
Console.Write("Resource Amount : " + infoList[i].Amount + "\n");
break; // REMOVE THIS LINE
}
break means that you're exiting from your for loop, so you're actually exiting as soon as you find a result.
How do I search and get results for partial matches i.e if I type scre
or screws?
You can change your if condition from
if (string.Compare(searchName, infoList[i].Resource, true) == 0)
to something like
if (infoList[i].Resource.ToLowerInvariant().Contains(searchName.ToLowerInvariant()))
This checks if the inserted string is contained in one of the strings that you have in your list. The ToLowerInvariant is used to make the search case-insensitive.
Of course, this is not a fuzzy-search, so searching for screws won't lead to any result. Doing fuzzy searches is a little bit more complex and it's probably outside the scope of this question.
Is there a better way of creating a console application where you
store values and a user searches for those values? I cannot find
anything close to what I need scouring the internet.
Opinion-based questions are not meant for StackOverflow, and asking a better way to do something is one of those. I don't think you'll get an answer on this.
To make it a less subjective, I'm going to interpret your last question as "Is there a common software pattern or approach that is used for storing data objects that can then be easily searched?"
In that regard, you're describing features and capabilities that a database is really good at, specifically storing some table of data and providing a fast way to search (query) it using criteria that you define.
C# and the .NET frameworks offer some pretty in-depth and robust tools for leveraging databases and managing mappings between data classes like JunkList and database tables; these types of tools are called ORMs, or Object Relational Mappers, and you'll see reference to them scattered throughout Microsoft's documentation, especially Entity Framework.
When getting started, more complicated ORMs can get a bit overwhelming, so it's not a bad idea to start small. Check out some very simple ORMs that use something like SQLite, which just live in a file and require minimal dependencies (the act of calling the database actually can be enough to create it!).
You might take a look at https://github.com/praeclarum/sqlite-net , which is very easy to set up in a matter of minutes and start using with just the basics provided in its documentation.
The reason you only get one result back is because of the break statement. It cancels the for loop. Try removing it.
Try looking at the String.Contains() method.

Hard Code List of Years?

This is the scenario. You've got a web form and you want to prompt the customer to select their birth year.
a) hard code the values in the dropdown list?
b) Grab valid years from a DB table
I can see a maintenance nightmare with copying a set of years hard coded in .aspx files everywhere.
updated:
for loop is not ideal (maintenance nightmare and error prone). The user then has to sift through 120 years that haven't even got here yet.
I still like the DB approach:
* Single point of data
* No duplication of code
* Update the table as needed to add more years
* Year table values could be used for some other dropdown for some other purpose entirely for something other than Birth year
Simple as that. No need to go updating code everywhere. I feel for data that is universal like this, we shouldn't be hard coding this shiza into a bunch of pages which is totally going against reuse and error prone...really it's not pratical. I'd take the hit to the DB for this.
Updated (again...after thinking about this):
Here's my idea. Just create a utility or helper method called GetYears that runs that loop and returns a List<int> back and I can bind that to whatever I want (dropdownlist, etc.). And I like the web.config idea of maintaining that end year.
C) Use a for-loop to generate the years in a range of your choice.
Something as simple as this pseudocode:
for (int i = 1900 ; i < THIS_YEAR - 13 ; i++)
{
validyears.options.Add(i);
}
Neither - provide a centralized service which can decide which mechanism to use, then the application doesn't care, and you are free to choose hardcoding, sliding window or database mechanisms.
To expand, typically, I would do something like this:
Define IPopulatableYear interface which has a single AddYear method taking an int and constructing an appropriate ListItem or whatever.
Make MyYearListBox inherit from regular ListBox implement IPopulatableYear (this works for winForms or WebForms)
Create static method or singleton or method in your DAL or whatever.
Like this:
PopulateYears(IPopulatableYear pl) {
// Very simple implementation - change at will
for (int lp = 2009 ; lp < 2009 + 10 ; lp++) {
pl.Add(lp);
}
}
or
PopulateYears(IPopulatableYear pl) {
// A DB implementation
SQLDataReader dr = DAL.YearSet() ; // Your choice of mechanism here
while ( dr.Read() ) {
pl.Add(dr[YEAR]);
}
}
or
PopulateYears(IPopulatableYear pl) {
// A DB limits implementation with different ranges defined in database by key - key determined by control itself - IPopulatableYear needs to implement a .YearSetKey property
SQLDataReader dr = DAL.YearLimits(pl.YearSetKey) ; // Your choice of mechanism here
for ( int lp = dr[YEAR_MIN] ; lp <= dr[YEAR_MAX] ; lp++ ) {
pl.Add(lp);
}
}
The mechanism is now centrally managed.
Use MyYearListBox on your forms and call PopulateYears() on it. If your forms are smart, they can detect all MyYearListBox instances and call it, so you no longer have any new code - just drag it on.
Take a look at Enumerable.Range. I think making DB calls is FAR less performant than Enumerable.Range.
E) Use a text input box, because that will always work.
(Be sure to validate it, of course, as a number. Include "Y2K" and "The year World War II started" in a dictionary of years, of course.)
How you present the year selection in the web form is irrelevant. It's an interface decision. Your server should not trust the data coming in, and should validate it accordingly. It's trivial to emulate a form submission, so it doesn't matter how it's presented. Heck, you can generate the drop down with javascript so there is no load on the server.
You can validate with a rule on the backend, rather than a lookup.
Since you're raising this whole issue (and making a bunch of comments), maybe it's within your power to think long and hard this.
For the end user, it's hard to beat the ease-of-use of a text box. Yup, you're going to get bogus data, but computers are supposed to make things easier, not harder. Scrolling through a long list of years to find the year I know I was born is a nuisance. Especially with all those young whippersnappers and old farts who want to enter birth years that aren't anywhere close to mine!
But stepping back even further...do you really need to ask the user their birth year in the first place? Is it that important to your application? Could you avoid the issue entirely by letting somebody else deal with that? Say by using OpenID, Windows Live ID or Facebook Connect?

Pluralising and Localizing strings in C#

I've got a C# WPF application I'm attempting to globalize with resx files. It works splendidly. I've run into a hitch, however. I've got a relatively simple solution for pluralisation where I have a singular and plural form of the string I'm displaying and I pick one based on the number of whatever things I'm talking about.
However, I've recently come to terms with the fact that some cultures have multiple plural forms. Has anyone come across a good solution to this problem in C#, let alone WPF?
The canonical example that I've seen for this problem comes from the Polish language. Polish has a singular, paucal, and plural form. This example is for the translation of the word "file":
1 plik
2,3,4 pliki
5-21 pliko'w
22-24 pliki
25-31 pliko'w
Mozilla has implemented this in Firefox 3, and they have a guide describing how to use their implementation here.
Most notably, in the Developing with PluralForm section, they have a link
resource://gre/modules/PluralForm.jsm
to the source of their implementation. Must be opened from within Firefox 3 and higher.
I have not read through the whole thing, but this seems to be like a good place to at least get some ideas.
HTH.
Consider trying to just avoid the problem altogether. Instead of building sentences, try and build your UI to avoid the problem. Instead of saying "5 pages" try saying: "Pages: 5".
Localization like this takes consideration of the languages you want to translate to. Multiple plurals are fairly rare and I would imagine in most cases are interchangeable or context sensitive. Which unless it is being used in multiple places in your application you will not need to worry about. If you are doing this and a particular usage does create a grammatical error in plural usage then you need to add a new key across the board (all languages) for that one instance. Or, since you are detecting culture anyway, add an additional conditional for the affected language and use the single alternate plural form.
I would not suggest avoiding it as you can quickly lose the flow of natural language "Mins answered ago: 6".
Maybe you meant this in the first place but the far more common scenario is variations in syntactical placement across different cultures. For example, the string wanting to be localized "This page is viewed X times". You may want to make 3 localizable strings for this:
PageViewStart = "This page is viewed"
PageViewEnd = "time"
PageViewEndPlural = "times"
Then a simple pseudo-implementation would be
PageViewStart + pageCount.ToString() + pageCount == 1 ? PageViewEnd : PageViewEndPlural;
However in Dutch "Deze pagina is {0} keer bekeken" and in Korean "조회수 {0}". So you see you will immediatley run into problems with implementations on the multiple ways to format plural sentence structure across languages.
I purposely left a {0} in the examples as it alludes to my solution. Use a localization for the whole sentence in plural and non-plural.
PageView = "This page viewed 1 time."
PageViewPlural = "This page viewed {0} times."
This way you can write the conditional (pseudo again depending on your implementation):
pageCount > 1 ? PageView : String.Format(PageViewPlural, pageCount.ToString());
The only thing is that your translators will need to be instructed as to the meaning and placement of the {0} token in the resx file.
I guess yo uare aware of gettext's plural form handling. But generally, I'd try to avoid it (as Yuliy said).
It's not only the nouns - phrases can change (e.g. in German "1 Datei konnte nicht gelöscht werden" / "2 Dateien konnten nicht gelöscht werden").
It is much more friendly and elegant than the problem-evasive "Dateien, die nicht gelöscht werden konnten: 2", but there's a tradeoff in how many ressources you have for localization.

Tag Cloud in C#

I am making a small C# application and would like to extract a tag cloud from a simple plain text. Is there a function that could do that for me?
Building a tag cloud is, as I see it, a two part process:
First, you need to split and count your tokens. Depending on how the document is structured, as well as the language it is written in, this could be as easy as counting the space-separated words. However, this is a very naive approach, as words like the, of, a, etc... will have the biggest word-count and are not very useful as tags. I would suggest implementing some sort of word black list, in order to exclude the most common and meaningless tags.
Once you have the result in a (tag, count) way, you could use something similar to the following code:
(Searches is a list of SearchRecordEntity, SearchRecordEntity holds the tag and its count, SearchTagElement is a subclass of SearchRecordEntity that has the TagCategory attribute,and ProcessedTags is a List of SearchTagElements which holds the result)
double max = Searches.Max(x => (double)x.Count);
List<SearchTagElement> processedTags = new List<SearchTagElement>();
foreach (SearchRecordEntity sd in Searches)
{
var element = new SearchTagElement();
double count = (double)sd.Count;
double percent = (count / max) * 100;
if (percent < 20)
{
element.TagCategory = "smallestTag";
}
else if (percent < 40)
{
element.TagCategory = "smallTag";
}
else if (percent < 60)
{
element.TagCategory = "mediumTag";
}
else if (percent < 80)
{
element.TagCategory = "largeTag";
}
else
{
element.TagCategory = "largestTag";
}
processedTags.Add(element);
}
I would really recommend using http://thetagcloud.codeplex.com/. It is a very clean implementation that takes care of grouping, counting and rendering of tags. It also provides filtering capabilities.
Take a look at http://sourcecodecloud.codeplex.com/
Here is an ASP.NET Cloud COntrol, that might help you at least get started, full source included.
You may want to take a look at WordCloud, a project on CodeProject. It includes 430 stops words (like the, an, a, etc.) and uses the Porter stemming algorithm, which reduces words to their root for so that "stemmed stemming stem" are all counted as 1 occurrence of the same word.
It's all in C# - the only thing you would have to do it modify it to output HTML instead of the visualization it creates.
Have a look at this answer for an algorithm:
Algorithm to implement a word cloud like Wordle
The "DisOrganizer" mentioned in the answers could serve your purpose. With a little change, you can let this "Disorganizer" to serve an image, the way you wanted. PS: The code is written in C# https://github.com/chandru9279/zasz.me/blob/master/zasz.me/
Take a look at this. It worked for me. There is a project under Examples folder named WebExample which will help you for solving this.
https://github.com/chrisdavies/Sparc.TagCloud
I'm not sure if this is exactly what your looking for but it may help you get started:
LINQ that counts word frequency(in VB but I'm converting to C# now)
Dim Words = "Hello World ))))) This is a test Hello World"
Dim CountTheWords = From str In Words.Split(" ") _
Where Char.IsLetter(str) _
Group By str Into Count()
You could store a category and the amount of items it has in some sort of collection, or database table.
From that, you can get the count for a certain category and have certain bounds. So your parameter is the category, and your return value is a count.
So if the count is >10 & <20, then apply a .CSS style to the link which will be of a certain size.
You can store these counts as keys in a collection, and then get the value where the key matches your return value (as I mentioned above).
I haven't got source code at hand for this process, but you won't find a simple function to do all this for you either. A control, yes (as above).
This is a very conventional approach and the standard way of doing it from what I've seen in magazine tutorials, etc, and the first approach I would think of (not necessarily the best).
The Zoomable TagCloud Generator which extracts keywords from a given source (text file and other sources) and displays the TagCloud as Zooming User Interface (ZUI)

Categories

Resources