[Résolu] String, map, reduce etc.

iLandesiLandes Membre
février 2015 modifié dans Objective-C, Swift, C, C++ #1

Je me penche sur la saisie d'une somme dans un UITextField... J'ai quelques questions


 


J'ai trouvé ce petit morceaux de code




let aString = "$123.56"
let replaced = String(map(aString.generate()) {
    $0 == "$" ? "+" : $0
    })


 

 


Il fonctionne le résultat dans replaced est +123.56


 


Je souhaiterais supprimer le $ et obtenir 123.56


 


J'ai tenté



import Cocoa

var currencyFormatter = NSNumberFormatter()
currencyFormatter.numberStyle = .CurrencyStyle
currencyFormatter.locale = NSLocale(localeIdentifier: "en_US")
let cs = currencyFormatter.currencySymbol

let aString = "$123.56"
let replaced = String(map(aString.generate()) {
    $0 == cs! ? "" : $0
    })

Et



import Cocoa

var currencyFormatter = NSNumberFormatter()
currencyFormatter.numberStyle = .CurrencyStyle
currencyFormatter.locale = NSLocale(localeIdentifier: "en_US")
let cs = currencyFormatter.currencySymbol

let aString = "$123.56"
let replaced = String(map(aString.generate()) {
    $0 == "$" ? "" : $0
    })

Je ne comprends pas l'erreur. Je débute en programmation fonctionnelle


 


J'ai une solution old school qui fonctionne



var currencyFormatter = NSNumberFormatter()
currencyFormatter.numberStyle = .CurrencyStyle
currencyFormatter.locale = NSLocale(localeIdentifier: "en_US")
let cs = currencyFormatter.currencySymbol

let replacedOldSchool = aString.stringByReplacingOccurrencesOfString(cs!, withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)

Mais je souhaite comprendre la programmation fonctionnelle


 


D'avance merci pour votre aide.


 


s


e


b


Réponses

  • AliGatorAliGator Membre, Modérateur
    Alors déjà  ton premier bloc de code de ton message doit être tronqué car le code n'a pas de sens et semble incomplet.


    Et ensuite c'est quoi l'erreur que tu as, sur quelle ligne ?
  • J'ai corrigé le premier bloque (erreur de copier / coller)


     


    Le message est "Could not find an overload for "==" that accept the supplied arguments"


  • AliGatorAliGator Membre, Modérateur
    février 2015 modifié #4

    Le message est "Could not find an overload for "==" that accept the supplied arguments"

    Ah ben voilà  ! Bon ça va l'erreur est très claire alors, c'est assez explicite ce qui ne va pas du coup vu le message.


    Il te dit juste qu'il ne sait pas faire un "==" entre "$0" et "cs!" (ou entre $0 et "$" dans ton autre test). Ce qui quand tu y penses est normal, car quand tu itères sur une String tu as des Character (donc $0 ici est de type Character)... et toi tu essayes de faire un == entre un Character et une String (ce qui n'est pas supporté, donc).


    Faut caster cs! en un Character (ou à  l'inverse caster $0 en une String) avant de pouvoir faire ton == tout simplement.
  • AliGatorAliGator Membre, Modérateur


    J'ai corrigé le premier bloque (erreur de copier / coller)

    Je pense que c'est toujours pas bon car je vois pas comment il génèrerait un "+" en sortie alors que ton code dans Map n'en contient pas...


  • Faut caster cs! en un Character (ou à  l'inverse caster $0 en une String) avant de pouvoir faire ton == tout simplement.




     


     


    Ok je comprends mais le problème est aussi au niveau de la chaà®ne vide "". Dès que je supprime le "+" et que je met "" l'erreur revient.


     


    Pour le moment j'ai ça



    import Cocoa

    var currencyFormatter = NSNumberFormatter()
    currencyFormatter.numberStyle = .CurrencyStyle
    currencyFormatter.locale = NSLocale(localeIdentifier: "en_US")
    let cs = Character(currencyFormatter.currencySymbol!)


    let aString = "$123.56"
    let replaced = String(map(aString.generate()) {
    $0 == cs ? "+" : $0
    })

  • AliGatorAliGator Membre, Modérateur
    Bah normal aussi...


    La closure / le bloc que tu passes à  Map prend un Character en entrée... et manifestement un Character en sortie. Dans le cas où ton test fait retourner $0 au bloc ça va. Dans le cas où tu retournes "+" à  la limite c'est une String mais j'imagine qu'il arrive quand même à  la casser tout seul en Character puisqu'il veut un Character en sortie et que "+" est justement une String d'un seul caractère.

    Mais si tu retournes "" alors tu ne peux pas dans ce cas créer un Character à  partir une chaà®ne vide (ça ne correspond à  aucun Character t'en as même pas dans cette chaà®ne "" de Character !)


    Map ne sait produire que des tableaux en sortie qui sont de la même taille que le tableau sur lequel Map est appliqué. En appliquant une fonction / closure sur chaque élément pour produire le nouveau. Là  tu pars d'une chaà®ne (une tableau de Character) et tu souhaites produire en sortie une chaà®ne d'une longueur potentiellement différente (un tableau de Character ayant un nombre différent d'éléments), forcément ça va pas coller.


    A la limite faut plutôt utiliser reduce() dans ce cas.
  • AliGatorAliGator Membre, Modérateur
    let str = "Hello World"

    let str2 = reduce(str.generate(), "") { outstr, c in
    return outstr + (c == "o" ? "OoO" : String(c))
    }
    // str2 vaut "HellOoO WOoOrld"
  • Au top  :p


     


    Ce qui donne pour supprimer le symbole monétaire d'une String en swift :



    // Playground - noun: a place where people can play

    import Cocoa

    var currencyFormatter = NSNumberFormatter()
    currencyFormatter.numberStyle = .CurrencyStyle
    currencyFormatter.locale = NSLocale(localeIdentifier: "en_US")
    let cs = currencyFormatter.currencySymbol!

    let aString = "$123.45"

    // Modern swift
    let replacedModern = reduce(aString.generate(), "") { outstr, c in
    return outstr + (c == Character(cs) ? "" : String(c))
    }

    // Old school
    let replacedOldSchool = aString.stringByReplacingOccurrencesOfString(cs, withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)


    :D


  • AliGatorAliGator Membre, Modérateur
    Tout à  fait.


    Bon après :


    1) j'aurais fait le cast de cs en Character à  l'extérieur du reduce (), de sorte que cs soit directement de type Character, c'est plus simple ensuite


    2) Dans ton cas particulier j'ai du mal à  comprendre l'intérêt d'utiliser un NSNumberFormatter en mode currency pour ensuite enlever le symbole monétaire de la chaà®ne produite...

    Soit tu utilises ton NSNumberFormatter en mode normal (non-currency) pour pas qu'il te mette le symbole monétaire, soit tu changes le currencySymbol de ton NSNumberFormatter pour le forcer à  "" avant de demander le formattage de ton nombre en chaà®ne... non ?
  • 1) Ok


    2) Je me sers de NSNumberFormatter en mode currency pour récupérer les réglages par défaut de l'utilisateur (séparateur de millier, symbol monétaire)


     


    Je cherche à  utiliser une chaine saisie par l'utilisateur qui doit être une somme mais si l'utilisateur n'a pas complètement respecté le format je veux tout de même pouvoir l'utiliser par exemple je veux accepter "12€" comme "12,00€" ou même "12". Je cherche donc à  débarrasser la chaine des éléments de formatage afin de la reformatter à  partir d'un double.


     


    Merci beaucoup pour ton aide


  • AliGatorAliGator Membre, Modérateur
    Donc si je saisis "€1€2,00€€" il voit ça comme 12€ ?
  • A ceci près que je n'autorise la saisie du symbole monétaire qu'une fois en début ou en fin de chaine  ::)


  • AliGatorAliGator Membre, Modérateur
    février 2015 modifié #14
    Donc je n'ai pas le droit d'écrire 12€34 par exemple ? Je suis obligé d'écrire ça 12.34€ ou €12.34 ?
  • c'est vrai que ce serait bien, je m'attaque à  cela demain 


  • AliGatorAliGator Membre, Modérateur
    Et sinon pourquoi construire un NSNumberFormatter, donc ? C'est uniquement pour récupérer le decimalSeparator et le currencySymbol et c'est tout ? Pourquoi ne pas demander directement ces éléments à  la NSLocale du coup ?


    let locale = NSLocale.currentLocale()
    let cs = locale.objectForKey(NSLocaleCurrencySymbol as! AnyObject) as? String
    let dc = locale.objectForKey(NSLocaleDecimalSeparator as! AnyObject) as? String
  • AliGatorAliGator Membre, Modérateur
    février 2015 modifié #17
    Et aussi, comment tu gères le cas où le currencySymbol n'est pas qu'un seul caractère mais plusieurs ?

    Exemple pour la Macédoine ( NSLocale(localeIdentifier: "mk_MK") ) ou pour la monnaie Tchèque ( NSLocale(localeIdentifier: "cs_CZ") ), etc... où leur currencySymbol est composé de 2 ou 3 caractères...
  • iLandesiLandes Membre
    février 2015 modifié #18

    Grrr "ден"


     


    Du coup je pense que je vais garder la méthode OldSchool




  • Et sinon pourquoi construire un NSNumberFormatter, donc ? C'est uniquement pour récupérer le decimalSeparator et le currencySymbol et c'est tout ? Pourquoi ne pas demander directement ces éléments à  la NSLocale du coup ?

     



    let locale = NSLocale.currentLocale()
    let cs = locale.objectForKey(NSLocaleCurrencySymbol as! AnyObject) as? String
    let dc = locale.objectForKey(NSLocaleDecimalSeparator as! AnyObject) as? String



     


    En plus des caractères des strings classiques : séparateur millier, décimal, symbol et je récupère aussi maximumFractionDigits (2 pour le $  ou € ou 0 pour le Yen certainement 3 dans certaines monnaies). Du coup par soucis de clarté je récupère tout de NSNumberFormatter

  • iLandesiLandes Membre
    février 2015 modifié #20

    Merci à  tous,


     


    J'ai finalement abandonné la saisie du symbole monétaire par l'utilisateur. Mes délégués permettent la saisie d'un nombre numérique avec séparateur décimal et un nombre de décimaux en relation avec les préférences monétaires de l'utilisateur. Une fois saisie le délégué transforme le nombre sous un format correspondant à  la monnaie locale de l'utilisateur.


     


    Un français peut saisir :


    • 12
    • 12,2
    • 12,45

    Il obtiendra


    • 12,00 €
    • 12,20 €
    • 12,45 €

     


    Un anglais peut saisir :


    • 12
    • 12.2
    • 12.45

    Il obtiendra


    • $ 12.00
    • $ 12.20
    • $ 12.45

    Etc. Pour plus d'information j'ai regroupé les informations collectées ici dans un projet sous GitHub. Vos appréciations et commentaires sont les bienvenus.


     


    Un grand merci à  tous


     


    http://ilandes.github.io/RSDCurrencyTextFieldSampleCode/


  • AliGatorAliGator Membre, Modérateur

    En plus des caractères des strings classiques : séparateur millier, décimal, symbol et je récupère aussi maximumFractionDigits

    Bah tout ça vient aussi de NSLocale. Regarde dans la doc de NSLocale, dans la section "Constantes" tu as toutes les clés pour ça, d'autant que c'est connu que l'instanciation d'un NSDateFormatter est un peu coûteux ;-)
  • J'ai regardé mais je n'ai pas trouvé le nombre de décimal après la virgule dans une somme


    Peut-être que je vois mal



    let NSLocaleIdentifier: String
    let NSLocaleLanguageCode: String
    let NSLocaleCountryCode: String
    let NSLocaleScriptCode: String
    let NSLocaleVariantCode: String
    let NSLocaleExemplarCharacterSet: String
    let NSLocaleCalendar: String
    let NSLocaleCollationIdentifier: String
    let NSLocaleUsesMetricSystem: String
    let NSLocaleMeasurementSystem: String
    let NSLocaleDecimalSeparator: String
    let NSLocaleGroupingSeparator: String
    let NSLocaleCurrencySymbol: String
    let NSLocaleCurrencyCode: String
    let NSLocaleCollatorIdentifier: String
    let NSLocaleQuotationBeginDelimiterKey: String
    let NSLocaleQuotationEndDelimiterKey: String
    let NSLocaleAlternateQuotationBeginDelimiterKey: String
    let NSLocaleAlternateQuotationEndDelimiterKey: String
Connectez-vous ou Inscrivez-vous pour répondre.