Am trying to update the Map<String,List<Map<String,String>>> invoices with the invoiceErrors as below

InvoiceError // is an entity with below attributes
{ String errorMessage,
  String invoiceNumber    
ErrorMessage                                          invoiceNumber   
-------------                                       -------------------     
File Error : The file is in an unsupported format   INV-Error_Test1
Line : 1 Invoice does not foot Reported             INV-Error_Test1
Line : 2 MATH ERROR                                 INV-Error_Test1
Line : 3 MATH ERROR                                 INV-Error_Test2
Line : 3 Invoice does not foot Reported             INV-Error_Test2

Am trying to achieve below map If the error message doesnt have a line number it need to be appended at the top level as invLineItems.put('error',['INV-Error_Test1' :File Error : The file is in an unsupported format]) Otherwise errormessage should be appended to the matching INVOICE and linenumber as below

invLineItems = [INV-Error_Test1:[[LINE:1, INVOICE_DATE:20150301, INVOICE_NUMBER:INV-Error_Test1, INVOICE_TOTAL:22, error : `Line : 1 Invoice does not foot Reported`], 
                                [LINE:2, INVOICE_DATE:20150301, INVOICE_NUMBER:INV-Error_Test1, INVOICE_TOTAL:24, error : `Line : 2 MATH ERROR`],
                INV-Error_Test2:[[LINE:3, INVOICE_DATE:20150301, INVOICE_NUMBER:INV-Error_Test2, INVOICE_TOTAL:26, , error : `Line : 3 MATH ERROR | Line : 3 Invoice does not foot Reported`], 
                                [LINE:4, INVOICE_DATE:20150301, INVOICE_NUMBER:INV-Error_Test2, INVOICE_TOTAL:28,]],
                error : [[INV-Error_Test1:`File Error : The file is in an unsupported format`]]

I wrote the below method to achieve the above

def regex = "^Line\\s(?:(\\d+)\\s)?\\s*:\\s+(\\d+)?.+";
for (e in invLineItems ){
  def errors =  lipErrors.findAll{it.invoiceNumber==e.key} // finding the error messages with the invoice number
  errors.each{  // fetching the line numbre from error message and finding the matching record the invoice number and line number in invLineItems 
     int lineNumber
     if (it.errorMessage.matches(regex)) {
            Pattern p = Pattern.compile("\\d+");
            Matcher m = p.matcher(it.errorMessage);
            if (m.find()) {
                 lineNumber = Integer.parseInt(m.group());
          println "lineNumber = "+lineNumber

    if(e.value['LINE_ITEM_NUMBER'].find{it==lineNumber.toString()}) {
      def data = lipErrors.findAll{it.invoiceNumber==e.key && it.errorMessage.matches("^Line\\s+"+lineNumber+"?\\:\\s+"+lineNumber+"?.+")}
      e.getValue().each{it.put("error", data.errorMessage.join("|"))}



The code doesnt look like Groovy and using traditional java code mostly, am wondering whether the code can be simplified with Groovy approach


1 Answer 1


You can use Groovy's regular expression operators to create Java Matchers.

def invoices = [
'5|20150301|INV WITH WHITESPACE|29,']    

def lines = invoices
    .findAll { it.contains('|') }
    .collect { it.tokenize('|') }

def headers = lines.first()
def invoiceMap =  lines.tail().collect{
    [headers, it].transpose().collectEntries()
}.groupBy{ it.INVOICE_NUMBER }

class InvoiceError {
    String errorMessage
    String invoiceNumber

    ['INV-Error_Test1', 'File Error : The file is in an unsupported format'],
    ['INV-Error_Test1', 'Line : 1 Invoice does not foot Reported'],
    ['INV-Error_Test1', 'Line : 2 MATH ERROR'],
    ['INV-Error_Test2', 'Line : 3 MATH ERROR'],
    ['INV-Error_Test2', 'Line : 3 Invoice does not foot Reported']
].collect {
    [['invoiceNumber', 'errorMessage'], it].transpose().collectEntries()
}.collect { it as InvoiceError }
.each {error ->
   def matcher = error.errorMessage =~ /Line : (\d+) (.*)/

   if(matcher) {
       def lineNumber = matcher.group(1)
       def errorDescription = matcher.group(2)
       def line = invoiceMap[error.invoiceNumber]
           .find() { it.LINE == lineNumber }

       if(line.errors) {
           line.errors = "$line.errors |  $errorDescription"
       } else {
           line.errors = errorDescription
   } else {
       if(!invoiceMap.errors) invoiceMap.errors = []
       invoiceMap.errors << [(error.invoiceNumber): error.errorMessage]


In this example, I build the InvoiceError instances from a list that's transposed (btw, thank you for that trick). Each resulting map is coerced into an instance of InvoiceError.

I took a different approach by iterating through the errors and inserting them into the invoiceMap, rather than iterating through the invoiceMap and looking up the errors.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.