Alternative à if, else if

J’ai beaucoup de déclarations if, sinon si et je sais qu’il doit exister un meilleur moyen de le faire, mais même après une recherche stackoverflow, je ne sais pas comment le faire dans mon cas particulier.

J’parsing des fichiers texte (factures) et assigne le nom du fournisseur de services à une variable (txtvar.Provider) en fonction du fait que certaines chaînes apparaissent sur la facture.

Ceci est un petit échantillon de ce que je fais (ne riez pas, je sais que c’est compliqué). En tout, il y a environ 300 si, sinon si.

if (txtvar.BillText.IndexOf("SWGAS.COM") > -1) { txtvar.Provider = "Southwest Gas"; } else if (txtvar.BillText.IndexOf("georgiapower.com") > -1) { txtvar.Provider = "Georgia Power"; } else if (txtvar.BillText.IndexOf("City of Austin") > -1) { txtvar.Provider = "City of Austin"; } // And so forth for many different ssortingngs 

J’aimerais utiliser quelque chose comme une instruction switch pour être plus efficace et plus lisible, mais je ne sais pas comment je comparerais BillText. Je cherche quelque chose comme ça mais je ne vois pas comment le faire fonctionner.

 switch (txtvar.BillText) { case txtvar.BillText.IndexOf("Southwest Gas") > -1: txtvar.Provider = "Southwest Gas"; break; case txtvar.BillText.IndexOf("TexasGas.com") > -1: txtvar.Provider = "Texas Gas"; break; case txtvar.BillText.IndexOf("Southern") > -1: txtvar.Provider = "Southern Power & Gas"; break; } 

Je suis définitivement ouvert aux idées.

EDIT: Pour répondre à la question supposée … Oui, j’aurais besoin de la capacité de déterminer l’ordre dans lequel les valeurs ont été évaluées. Comme vous pouvez l’imaginer, lors de l’parsing de centaines de dispositions légèrement différentes, je suis parfois confronté à la question de ne pas avoir d’indicateur distinctement unique indiquant le fournisseur de services auquel le projet de loi appartient. (Merci pour toutes les suggestions géniales! Je suis sorti du bureau depuis quelques jours et j’essaierai de les essayer dès que possible)

Pourquoi ne pas utiliser tout ce que C # a à offrir? L’utilisation suivante des types anonymes, des initialiseurs de collection, des variables implicites et de la syntaxe lambda LINQ est compacte, intuitive et maintient votre exigence modifiée selon laquelle les modèles doivent être évalués dans l’ordre:

 var providerMap = new[] { new { Pattern = "SWGAS.COM" , Name = "Southwest Gas" }, new { Pattern = "georgiapower.com", Name = "Georgia Power" }, // More specific first new { Pattern = "City of Austin" , Name = "City of Austin" }, // Then more general new { Pattern = "Austin" , Name = "Austin Elecsortingc Company" } // And for everything else: new { Pattern = Ssortingng.Empty , Name = "Unknown" } }; txtVar.Provider = providerMap.First(p => txtVar.BillText.IndexOf(p.Pattern) > -1).Name; 

Plus probablement, les paires de modèles proviendraient d’une source configurable, telle que:

 var providerMap = System.IO.File.ReadLines(@"C:\some\folder\providers.psv") .Select(line => line.Split('|')) .Select(parts => new { Pattern = parts[0], Name = parts[1] }).ToList(); 

Enfin, comme @millimoose le souligne, les types anonymes sont moins utiles lorsqu’ils sont transmis entre méthodes. Dans ce cas, nous pouvons définir une classe de Provider sortingval et utiliser des initialiseurs d’object pour une syntaxe presque identique:

 class Provider { public ssortingng Pattern { get; set; } public ssortingng Name { get; set; } } var providerMap = System.IO.File.ReadLines(@"C:\some\folder\providers.psv") .Select(line => line.Split('|')) .Select(parts => new Provider() { Pattern = parts[0], Name = parts[1] }).ToList(); 

Comme il semble que vous ayez besoin de rechercher la clé avant de renvoyer la valeur, un Dictionary est la bonne solution, mais vous devrez la parcourir en boucle.

 // dictionary to hold mappings Dictionary mapping = new Dictionary(); // add your mappings here // loop over the keys foreach (KeyValuePair item in mapping) { // return value if key found if(txtvar.BillText.IndexOf(item.Key) > -1) { return item.Value; } } 

EDIT: Si vous souhaitez contrôler l’ordre dans lequel les éléments sont évalués, utilisez un OrderedDictionary et ajoutez les éléments dans l’ordre dans lequel vous souhaitez les évaluer.

Un de plus en utilisant LINQ et Dictionnaire

 var mapping = new Dictionary() { { "SWGAS.COM", "Southwest Gas" }, { "georgiapower.com", "Georgia Power" } . . }; return mapping.Where(pair => txtvar.BillText.IndexOf(pair.Key) > -1) .Select(pair => pair.Value) .FirstOrDefault(); 

Si nous préférons une chaîne vide au lieu de null lorsqu’aucune clé ne correspond, nous pouvons utiliser le ?? opérateur:

 return mapping.Where(pair => txtvar.BillText.IndexOf(pair.Key) > -1) .Select(pair => pair.Value) .FirstOrDefault() ?? ""; 

Si nous considérons que le dictionnaire contient des chaînes similaires, nous ajoutons un ordre en ordre alphabétique, la clé la plus courte sera la première, elle sélectionnera ‘SCE’ avant ‘SCEC’

 return mapping.Where(pair => txtvar.BillText.IndexOf(pair.Key) > -1) .OrderBy(pair => pair.Key) .Select(pair => pair.Value) .FirstOrDefault() ?? ""; 

Pour éviter la flagrante Schlemiel, l’approche du peintre, qui consiste à boucler sur toutes les clés, impliquerait: utilisons des expressions régulières!

 // a dictionary that holds which bill text keyword maps to which provider static Dictionary BillTextToProvider = new Dictionary { {"SWGAS.COM", "Southwest Gas"}, {"georgiapower.com", "Georgia Power"} // ... }; // a regex that will match any of the keys of this dictionary // ie any of the bill text keywords static Regex BillTextRegex = new Regex( ssortingng.Join("|", // to alternate between the keywords from key in BillTextToProvider.Keys // grab the keywords select Regex.Escape(key))); // escape any special characters in them /// If any of the bill text keywords is found, return the corresponding provider. /// Otherwise, return null. ssortingng GetProvider(ssortingng billText) { var match = BillTextRegex.Match(billText); if (match.Success) // the Value of the match will be the found subssortingng return BillTextToProvider[match.Value]; else return null; } // Your original code now reduces to: var provider = GetProvider(txtvar.BillText); // the if is be unnecessary if txtvar.Provider should be null in case it can't be // determined if (provider != null) txtvar.Provider = provider; 

Rendre cette distinction insensible à la casse est un exercice sortingvial pour le lecteur.

Cela étant dit, cela ne prétend même pas imposer un ordre sur lequel les mots-clés doivent être recherchés en premier lieu: il trouvera la correspondance trouvée le plus tôt dans la chaîne. (Et ensuite celui qui apparaît en premier dans l’ER.) Vous mentionnez cependant que vous cherchez dans des textes de grande taille; si la mise en œuvre RE de .NET est bonne, elle devrait être considérablement supérieure à 200 recherches de chaînes naïves. (En ne faisant qu’un passage dans la chaîne, et peut-être un peu en fusionnant les préfixes communs dans le RE compilé.)

Si la commande est importante pour vous, vous pouvez envisager de rechercher une implémentation d’un meilleur algorithme de recherche de chaînes que celui utilisé par .NET. (Comme une variante de Boyer-Moore.)

Ce que vous voulez, c’est un dictionnaire :

 Dictionary mapping = new Dictionary(); mapping["SWGAS.COM"] = "Southwest Gas"; mapping["foo"] = "bar"; ... as many as you need, maybe read from a file ... 

Alors juste:

 return mapping[inputSsortingng]; 

Terminé.

Une façon de le faire (d’autres réponses montrent des options très valables):

 void Main() { ssortingng input = "georgiapower.com"; ssortingng output = null; // an array of ssortingng arrays...an array of Tuples would also work, // or a List with any two-member type, etc. var search = new []{ new []{ "SWGAS.COM", "Southwest Gas"}, new []{ "georgiapower.com", "Georgia Power"}, new []{ "City of Austin", "City of Austin"} }; for( int i = 0; i < search.Length; i++ ){ // more complex search logic could go here (eg a regex) if( input.IndexOf( search[i][0] ) > -1 ){ output = search[i][1]; break; } } // (optional) check that a valid result was found. if( output == null ){ throw new InvalidOperationException( "A match was not found." ); } // Assign the result, output it, etc. Console.WriteLine( output ); } 

La principale chose à retirer de cet exercice est de créer un switch géant ou une structure if/else n’est pas la meilleure façon de le faire.

Il existe plusieurs approches pour ce faire, mais pour des raisons de simplicité, l’ opérateur conditionnel peut être un choix:

 Func contains=x => { return txtvar.BillText.IndexOf(x)>-1; }; txtvar.Provider= contains("SWGAS.COM")?"Southwest Gas": contains("georgiapower.com")?"Georgia Power": contains("City of Austin")?"City of Austin": // more statements go here // if none of these matched, txtvar.Provider is assigned to itself txtvar.Provider; 

Notez que le résultat est conforme à la condition plus précédée qui est remplie, donc si txtvar.BillText="City of Austin georgiapower.com"; alors le résultat serait "Georgia Power" .

vous pouvez utiliser le dictionnaire.

 Dictionary textValue = new Dictionary(); foreach (KeyValuePair textKey in textValue) { if(txtvar.BillText.IndexOf(textKey.Key) > -1) return textKey.Value; }