Copy part of the properties to another object

Sometimes, we need to copy part of the properties of an object into a different one. 
The simplest thing would be to do the following:

Set obj1.FirstName = obj2.FirstName

Set obj1.SecondName = obj2.SecondName

What happens if the object contains a large number of properties? or we just want to extract an important group of data, and complement the information in another object?

Having the following classes:

Class Kurro.PersonalInfo Extends %Library.SerialObject
{
 /// Code of personal
 Property Code As %String;
 
 /// First name of personal
 Property FirstName As %String;
 
 /// Second name of personal
 Property SecondName As %String;
 
 /// Date of birthday of personal
 Property DateOfBirthday As %DateTime;
 
 /// Passport ID number
 Property PassportId As %String;
  
}



Class Kurro.NameInfo Extends %Library.SerialObject
{

 /// First name of personal
 Property FirstName As %String;
 
 /// Second name of personal
 Property SecondName As %String;
 
 /// Date of birthday of personal
 Property DateOfBirthday As %DateTime;

 /// Relationship
 Property Relationship As %String;
 
}

I've populated the object PersonalInfo

ENSDEMO>zw personal
personal=<OBJECT REFERENCE>[1@Kurro.PersonalInfo]
+----------------- general information ---------------
|      oref value: 1
|      class name: Kurro.PersonalInfo
| reference count: 2
+----------------- attribute values ------------------
|               Code = "FLH01"
|     DateOfBirthday = 47878
|          FirstName = "Francisco"
|         PassportId = "ESP-123456"
|         SecondName = "Lopez"
+-----------------------------------------------------

I've created the following utils class

Class Kurro.Utils.Functions
{

/// Copy properties 
Method CopyProps(Output obj) As %Status [ CodeMode = objectgenerator ]
{
    set properties = %compiledclass.Properties
    for i=1:1:properties.Count() 
    {
        #dim prop As %Dictionary.CompiledProperty = properties.GetAt(i)
        if prop.Name'="",prop.Name'["%" 
        {
            set propName = prop.Name
            if prop.Name["_" 
            {
                set propName = """"_prop.Name_""""
            }
            do %code.WriteLine(" If (obj.ExistsProperty("""_propName_""")) { set obj."_propName _"=.."_propName_" } ")
        }
    }
    quit $$$OK
}

/// Check if a property exists
ClassMethod ExistsProperty(pPropName As %String = "") As %Boolean
{
    Set tExists = 0
    If (pPropName '= "") 
    {
        Set tExists = ##class(%Dictionary.CompiledProperty).IDKEYExists($classname($this),pPropName)
    }
    Quit tExists
}

}

So now we need to extend our classes also to this utils class

Class Kurro.PersonalInfo Extends (%Library.SerialObject, Kurro.Utils.Functions)
Class Kurro.NameInfo Extends (%Library.SerialObject, Kurro.Utils.Functions)

You can check how it works using the following code:

set personal = ##class(Kurro.PersonalInfo).%New()
set personal.Code="FLH01"
set personal.FirstName="Francisco"
set personal.SecondName="Lopez"
set personal.DateOfBirthday=$ZDATEH("1972-02-01",3)
set personal.PassportId ="ESP-123456"
set name = ##class(Kurro.NameInfo).%New()
do personal.CopyProps(.name)

zw name
    

This is the result:

ENSDEMO>zw name 
name=<OBJECT REFERENCE>[3@Kurro.NameInfo]
+----------------- general information ---------------
|      oref value: 3
|      class name: Kurro.NameInfo
| reference count: 2
+----------------- attribute values ------------------
|     DateOfBirthday = 47878
|          FirstName = "Francisco"
|       Relationship = ""
|         SecondName = "Lopez"
+-----------------------------------------------------

Important: Only you can copy content to a class that extends to this functions, because it needs the method ExistsProperty.

I hope this trick is as useful to you as it has been to me

Best regards.

Update: This account is discontinued, please follow the new account: @Francisco López 

Comments

Nice example of using method generators.

Why do you need ExistsProperty? Here's a check I usually employ for class properties in generators:

if prop.Internal || prop.Calculated || prop.ReadOnly || prop.Private || prop.Identity || prop.MultiDimensional  CONTINUE

It filters out all systems properties like Concurrency, %%OID and so on.

Hi,

I've created this method to check if a property exitsts because the method generator doesn't knows if the property exists in the target class in runtime.

If you debug the code you can see the compiled code:

zCopyProps(obj) public {
 If (obj.ExistsProperty("Code")) set obj.Code=..Code 
 If (obj.ExistsProperty("DateOfBirthday")) set obj.DateOfBirthday=..DateOfBirthday 
 If (obj.ExistsProperty("FirstName")) set obj.FirstName=..FirstName 
 If (obj.ExistsProperty("PassportId")) set obj.PassportId=..PassportId 
 If (obj.ExistsProperty("SecondName")) set obj.SecondName=..SecondName }

so, in runtime, there is no a quick way to check if this property exists to prevent an exception error.

Why do you need to check it at runtime? If source and target are from different but related  classes you can copy by their common ancestor (or by specified ancestor class). Or do you copy properties between two unrelated classes?

The idea is to copy only the common properties.

If you see the definition of PersonalInfo and NameInfo, both are different classes.

The unique union between both classes is Kurro.Utils.Functions

He does not know what properties the target class obtained, he only knows his own properties and makes a list of methods to copy from the current class to the target class.

This method is compiled, therefore, you need to verify if the destination property exists in the target class at run time. This method is created "on fly" when you create a new instance of this class. Do you know if the property "Name" will be in the object passed as a parameter? Maybe not, so you should check if this property exists in the target.

makes sense?

I hope I have solved the doubt, or maybe I do not understand your question.

Nice one, just notice that your util can copy properties between different types of objects.

Did you checked it with multi dim properties, relationships, arrays, lists, serials,  streams, calculated, etc.?

Notice that if obj1 and obj2 are of the same class then use %ConstructClone. It is part of the %Library.RegisteredObject class

http://docs.intersystems.com/latest/csp/docbook/%25CSP.Documatic.cls?PAGE=CLASS&LIBRARY=%25sys&CLASSNAME=%25Library.RegisteredObject#METHOD_%ConstructClone

Hi Yuval,

Thanks for your comment.

The idea of this tip is only copy content from a class to another class, only I want a subgroup of elements. The command %ConstructClone is to clone a class, you have the same class, not a part of it.

Best regards