2015-10-21 3 views
7

कुछ नेस्टेड मामले वर्गों और क्षेत्र addresses एक Seq[Address] है:"सेक" फ़ील्ड के साथ इस नेस्टेड केस क्लास को कैसे संशोधित करें?

val employee = Employee(Company(Seq(
    Address(Street("aaa street")), 
    Address(Street("bbb street")), 
    Address(Street("bpp street"))))) 

यह 3 पते हैं:

// ... means other fields 
case class Street(name: String, ...) 
case class Address(street: Street, ...) 
case class Company(addresses: Seq[Address], ...) 
case class Employee(company: Company, ...) 

मैं एक कर्मचारी है।

और मैं केवल सड़कों को "बी" से शुरू करना चाहता हूं। मेरे कोड निम्नलिखित की तरह गड़बड़ है:

val modified = employee.copy(company = employee.company.copy(addresses = 
    employee.company.addresses.map { address => 
     address.copy(street = address.street.copy(name = { 
      if (address.street.name.startsWith("b")) { 
      address.street.name.capitalize 
      } else { 
      address.street.name 
      } 
     })) 
     })) 

modified कर्मचारी तो है:

Employee(Company(List(
    Address(Street(aaa street)), 
    Address(Street(Bbb street)), 
    Address(Street(Bpp street))))) 

मैं इसे बेहतर बनाने के लिए एक तरह से तलाश कर रहा हूँ, और एक नहीं मिल रहा। यहां तक ​​कि Monocle भी कोशिश की, लेकिन इस समस्या पर इसे लागू नहीं कर सकता है।

क्या इसे बेहतर बनाने का कोई तरीका है?


पुनश्च: वहाँ दो महत्वपूर्ण आवश्यकताओं हैं:

  1. उपयोग केवल अपरिवर्तनीय डेटा
  2. अन्य मौजूदा क्षेत्रों

उत्तर

13

पीटर Neyens बताते हैं के रूप में, निराकार के SYB वास्तव में अच्छी तरह से यहाँ काम करता है, लेकिन यह, पेड़ में सभीStreet मूल्यों को संशोधित करेगा जो हमेशा नहीं हो सकता जो आप चाहते हैं वही बनें। आप पथ पर अधिक नियंत्रण की जरूरत है, मोनोकल मदद कर सकते हैं:

import monocle.Traversal 
import monocle.function.all._, monocle.macros._, monocle.std.list._ 

val employeeStreetNameLens: Traversal[Employee, String] = 
    GenLens[Employee](_.company).composeTraversal(
    GenLens[Company](_.addresses) 
     .composeTraversal(each) 
     .composeLens(GenLens[Address](_.street)) 
     .composeLens(GenLens[Street](_.name)) 
) 

    val capitalizer = employeeStreeNameLens.modify { 
    case s if s.startsWith("b") => s.capitalize 
    case s => s 
    } 

जुलिएन त्रुफाउट एक संपादन में बताते हैं के रूप में, आप इस और भी अधिक संक्षिप्त (लेकिन कम सामान्य) कर सकते हैं करने के लिए एक लेंस सभी तरह बनाने के द्वारा सड़क का नाम का पहला वर्ण:

import monocle.std.string._ 

val employeeStreetNameFirstLens: Traversal[Employee, Char] = 
    GenLens[Employee](_.company.addresses) 
    .composeTraversal(each) 
    .composeLens(GenLens[Address](_.street.name)) 
    .composeOptional(headOption) 

val capitalizer = employeeStreetNameFirstLens.modify { 
    case 'b' => 'B' 
    case s => s 
} 

प्रतीकात्मक ऑपरेटरों कि एक छोटे से अधिक संक्षिप्त उपरोक्त परिभाषाओं होगा रहे हैं, लेकिन मैं गैर प्रतीकात्मक संस्करण पसंद करते हैं।

और फिर (परिणाम स्पष्टता के लिए पुन: स्वरूपित के साथ):

scala> capitalizer(employee) 
res3: Employee = Employee(
    Company(
    List(
     Address(Street(aaa street)), 
     Address(Street(Bbb street)), 
     Address(Street(Bpp street)) 
    ) 
) 
) 

ध्यान दें कि निराकार जवाब के रूप में, आप Seq के बजाय List उपयोग करने के लिए अपने Employee परिभाषा को बदलने के लिए या यदि आप की आवश्यकता होगी, डॉन अपने मॉडल को बदलना नहीं चाहते हैं, आप उस परिवर्तन को Lens में Iso[Seq[A], List[A]] के साथ बना सकते हैं।

8

खोना नहीं है आप addresses में जगह के लिए खुले हैं, तो CompanySeq से List पर, आप आकारहीन () से "अपना बॉयलरप्लेट स्क्रैप" का उपयोग कर सकते हैं 210)।

import shapeless._, poly._ 

case class Street(name: String) 
case class Address(street: Street) 
case class Company(addresses: List[Address]) 
case class Employee(company: Company) 

val employee = Employee(Company(List(
    Address(Street("aaa street")), 
    Address(Street("bbb street")), 
    Address(Street("bpp street"))))) 

आप एक बहुरूपी समारोह जो एक Street के नाम फ़ायदा उठाने की अगर नाम एक "बी" के साथ शुरू होता बना सकते हैं।

object capitalizeStreet extends ->(
    (s: Street) => { 
    val name = if (s.name.startsWith("b")) s.name.capitalize else s.name 
    Street(name) 
    } 
) 

के रूप में आप किसका उपयोग कर सकते हैं:

val afterCapitalize = everywhere(capitalizeStreet)(employee) 
// Employee(Company(List(
// Address(Street(aaa street)), 
// Address(Street(Bbb street)), 
// Address(Street(Bpp street))))) 
+1

धन्यवाद यह कर सकता है !!! यह वास्तव में अच्छा है। मुझे अंततः यह जानने का मौका मिला कि कितना शक्तिशाली बेकार है! – Freewind

+3

अच्छा जवाब, लेकिन एक चेतावनी के लिए मेरा देखें (यह डेटा संरचना में _any_ सड़क नामों को बदल देगा)। –

2

quicklens

पर एक नजर डालें तुम इतना की तरह इस

import com.softwaremill.quicklens._ 

case class Street(name: String) 
case class Address(street: Street) 
case class Company(address: Seq[Address]) 
case class Employee(company: Company) 
object Foo { 
    def foo(e: Employee) = { 
    modify(e)(_.company.address.each.street.name).using { 
     case name if name.startsWith("b") => name.capitalize 
     case name => name 
    } 
    } 
} 
संबंधित मुद्दे