Skip to Content

In Horst’s blog ABAP News for Release 7.50 โ€“ CORRESPONDING, againย the usage of CL_ABAP_CORRESPONDING is introduced.

There is already a demo program in SAP help.
I write another simple one and I will keep using that scenario in Java and JavaScript as well.

CL_ABAP_CORRESPONDING

Suppose there are two developers Jerry and Tom who mainly focus on ABAP and Java. Their salary are stored in the internal table developer_list. Later they would like to work as presale for new challenge and the corresponding information should be moved to another internal table presale_list. The content of these two internal table should be equal, unfortunately the field name are slightly different. In this case it is show time for CL_ABAP_CORRESPONDING:
Report z.
TYPES: BEGIN OF developer,
         focus_language TYPE string,
         salary         TYPE i,
         name           TYPE string,
       END OF developer,

       BEGIN OF presale,
         focus_area        TYPE string,
         salary_plus_bonus TYPE i,
         name              TYPE string,
       END OF presale.

DATA: developer_list TYPE TABLE OF developer WITH EMPTY KEY,
      presale_list   TYPE TABLE OF presale WITH EMPTY KEY.

developer_list = VALUE #(
  ( name = 'Jerry' focus_language = 'ABAP' salary = 2000 )
  ( name = 'Tom' focus_language = 'Java' salary = 2050 ) ).

presale_list = VALUE #( ( name = 'Jerry'  ) ( name = 'Tom'  ) ).

DATA(lo_mapping_executor) = cl_abap_corresponding=>create(
  source              = developer_list
  destination       = presale_list
  mapping           = VALUE cl_abap_corresponding=>mapping_table(
   ( level = 0 kind = 1 srcname = 'focus_language' dstname = 'focus_area' )
   ( level = 0 kind = 1 srcname = 'salary' dstname = 'salary_plus_bonus' ) ) ).

lo_mapping_executor->execute( EXPORTING source      = developer_list
                              CHANGING  destination = presale_list ).
The data transfer from developer internal table to presales internal table consists of two steps:
1. configure mapping information using CL_ABAP_CORRESPONDING=>CREATE, a mapping executor instance is returned.
2. call mapping executor’s method execute to get mapped internal table.
The mapped result in this example looks as below:

CL_JAVA_CORRESPONDING

Still use the same scenario, here is my definition of Developer and Presale:
public class Developer {
	private String focusLanguage;
	private int salary;
	private String name;
	public Developer(String name, String language, int salary){
		this.name = name;
		this.focusLanguage = language;
		this.salary = salary;
	}
}
public class PreSales {
	private String name;
	private String focusArea;
	private int salaryPlusBonus;
	
	public PreSales(String name){
		this.name = name;
	}
	public String toString(){
		return "Presales: " + this.name + " focusArea: " + this.focusArea + " salaryPlusBonus: " + 
	         this.salaryPlusBonus;
	}
}
And let’s do some enhancement on top of the scenario. Suppose after Jerry and Tom work as a Presale, they get their salary doubled ( this is just an example, not the real case in SAP of course ! ) It is expected such salary double could also be automatically performed during mapping execution.
This is my test code in Java:
List<Developer> developers = new ArrayList<Developer>();
		developers.add(new Developer("Jerry", "ABAP", 2000));
		developers.add(new Developer("Tom", "Java", 2050));

		List<PreSales> preSales = new ArrayList<PreSales>();
		preSales.add(new PreSales("Jerry"));
		preSales.add(new PreSales("Tom"));
Above code just constructs a list for developer and a list for presales.
		
		CL_MAPPING[] mapping = new CL_MAPPING[2];		
		mapping[0] = new CL_MAPPING("focusLanguage", "focusArea", null);
And above line defines the first mapping rule, to map field focusLanguage of developer to field focusArea of Presale.
Function<Integer, Integer> salaryDouble = e -> e * 2;
mapping[1] = new CL_MAPPING("salary", "salaryPlusBonus", salaryDouble);
Above line also defines a mapping rule using a Function instance salaryDouble. The implementation of this function is easy to understand: double the salary!
For comparison purpose I still stick to ABAP naming convention in this Java code, you see the consumption pattern for CL_JAVA_CORRESPONDING is exactly as what we have done in previous ABAP example: first configure mapping rule and get a mapping executor, and then call its execute method to perform the mapping.
CL_JAVA_CORRESPONDING mappingExecutor = CL_JAVA_CORRESPONDING.CREATE(developers, preSales, mapping);
List<PreSales> mappedPresales = (List<PreSales>) mappingExecutor.execute();
mappedPresales.forEach(System.out::println);
Mapping result, the salary for both guy are doubled ๐Ÿ™‚
Below is the core method for mapping logic done in Java.
The idea is simple, get the value of source field via Java reflection, and set to the corresponding field of target object as well.
The complete implementation and test code of CL_JAVA_CORRESPONDING could be found from my github.

CL_JS_CORRESPONDING

Due to the dynamic programming trait of JavaScript, it is pretty easier to implement the same logic in JavaScript.
Again the definition of Developer and Presale:
function Developer(name, language, salary){
	this.name = name;
	this.focusLanguage = language;
	this.salary = salary;
}

function Presales(name){
	this.name = name;
}
And implementation of CL_JS_CORRESPONDING ( I use ABAP naming convention once again here )
var CL_JS_CORRESPONDING = function() {  
 function MappingExecutor(src, target, mapping){
 	this.src = src;
 	this.target = target;
 	this.mapping = mapping;
 	function _map(source, target, mapping){
 		for( var i = 0; i < source.length; i++){
 			_mapEach(source[i], target[i], mapping);
 		}
 	}
 	function _mapEach(source, target, mapping){
 		target[mapping.target] = source[mapping.source];
 		if( mapping.function){
 			target[mapping.target] = mapping.function.call(null, target[mapping.target]);
 		}
 	}
 	MappingExecutor.prototype.execute = function(){
 		for( var i = 0; i < this.mapping.length; i++){
 			_map(this.src, this.target, this.mapping[i]);
 		}
 		return this.target;
 	}
 };
 return { 
    CREATE:function(src,target,mapping){
   	  if( !Array.isArray(src) || !Array.isArray(target)){
   	  	return target;
   	  }
   	  if( src.length != target.length){
   	  	return target;
   	  }
   	  if( src.length == 0){
   	  	return target;
   	  }
   	  return new MappingExecutor(src, target, mapping);
  }
}}(); 
Consumer code below. Here I use the arrow function to define the salary double rule:
var developerList = [ new Developer("Jerry", "ABAP", 2000),
                                  new Developer("Tom",   "Java", 2050)];
var preSalesList  = [ new Presales("Jerry"), 
                      new Presales("Tom")]; 
var mapping = [
	{
		source:"focusLanguage",
		target:"focusArea"
	},
	{
		source:"salary",
		target:"salaryPlusBonous",
		function: x => { return x * 2 }
	}
];
var mappingExecutor = CL_JS_CORRESPONDING.CREATE(developerList, preSalesList, mapping);
var mappedPreSales = mappingExecutor.execute();
Execution result: salary doubled, again!
All source code of JavaScript version could be found from my github here.

Rewrite Javascript implementation using functional programming style

According to David ‘s comment since in JavaScript the functional programming is possible, why not try it?

Then I rewrite the Javascript implementation using map provided for Array. Here below is the source code:

var CL_JS_CORRESPONDING = function() { 
  
 function MappingExecutor(src, target, mapping){
 	this.src = src;
 	this.target = target;
 	this.mapping = mapping;

 	MappingExecutor.prototype.execute = function(){
    var mapCurrentTarget = function(currentTarget, currentSource, mapping){
      mapping.map(function(currentMapping){
        this.currentTarget[currentMapping.target] = this.currentSource[currentMapping.source];
        if( currentMapping.function) {
           this.currentTarget[currentMapping.target] = currentMapping.function.call(null, this.currentTarget[currentMapping.target]);
        }
      }, {
           currentTarget: currentTarget,
           currentSource: currentSource
      });
      return currentTarget;
    }
    var mappingFunctor = function (currentTarget, index){ 
      return mapCurrentTarget(currentTarget, this.src[index], this.mapping);
    };
    return this.target.map(mappingFunctor, this);
 	}
 };
 return { 
    CREATE:function(src,target,mapping){
   	  if( !Array.isArray(src) || !Array.isArray(target)){
   	  	return target;
   	  }
   	  if( src.length != target.length){
   	  	return target;
   	  }
   	  if( src.length == 0){
   	  	return target;
   	  }
   	  return new MappingExecutor(src, target, mapping);
  }
}}(); 
This version has 40 lines of source code, you can compare it with the old style implementation using for loop, which has 37 lines of code.
Meanwhile in ABAP we can also simulate “Functional Programming” a little bit. See this blog Functional Programming โ€“ Try Reduce in JavaScript and in ABAP for detail.

Further reading

I have written a series of blogs which compare the language feature among ABAP, JavaScript and Java. You can find a list of them below:
To report this post you need to login first.

5 Comments

You must be Logged on to comment or reply to a post.

  1. David Kunz

    Hi!

     

    I don’t think it is a good idea to write JavaScript in an ‘ABAP style’.
    For example, I would avoid ‘for (int i = 0, ….)’ loops and use a combination of reduce, map and filter.

    If you are interested in a more functional style, which comes in very handy in this context, take a look at the Ramda package andย https://github.com/MostlyAdequate/mostly-adequate-guide .

     

    Best regards

    David

    (0) 
    1. Jerry Wang Post author

      Hello David,

      Yes you are right, I will try to re-implement the execute method of JavaScript version using more pure JavaScript style.

      And really appreciate your link to the nice book ๐Ÿ™‚

      Best regards,

      Jerry

      (0) 
    2. Jerry Wang Post author

      Hello David,

      I have reimplemented the JavaScript version using Array.map. Would you please kindly have a look again? ๐Ÿ™‚

      Best regards,

      Jerry

       

      (0) 
      1. David Kunz

        Hi Jerry!

         

        Using map is a start ๐Ÿ™‚

        One critical aspect of functional programming is to use pure functions (https://en.wikipedia.org/wiki/Pure_function) wherever possible. I think you try to force the abap functionality too much in your application (i.e. the consumer code).

        There is a nice video which shows Ramda by example:ย https://www.youtube.com/watch?v=tN4wyJ9DdtM

        I think you can benefit a lot from this!

        Keep up the good work!

        Best regards

        David

        (0) 
        1. Jerry Wang Post author

          Hello David,

          Thanks a lot for your comment. Yes the aim of this blog is to simulate the corresponding functionality supported by ABAP with other programming language while keeping the same signature. Nevertheless I will study the video you provided to learn more.

          Have a nice day ๐Ÿ™‚

          Best regards,

          Jerry

          (0) 

Leave a Reply