Dto Pattern: Best Way to Copy Properties Between Two Objects

Elegant way to copy properties from object to another

Automapper has been out there since ages ago. Tried and tested. http://automapper.org/

Here's an example:

using System;
using AutoMapper;

public class Program
{
class SupplierInfo
{
public SupplierInfo( string name, string shortName, string brandName ) {
Name = name;
ShortName = shortName;
BrandName = brandName;
}
public string Name {get; private set; }
public string ShortName {get; private set; }
public string BrandName {get; private set; }
}

class Supplier
{
public string name {get; set; }
public string shortName {get; set; }
public string brandName {get; set; }
}

public static void Main()
{
var dto = new Supplier() {
name = "Name 1",
shortName = "Short Name 1",
brandName = "Brand Name 1"
};


//From the tutorial:
//You only need one MapperConfiguration instance typically per AppDomain and should be instantiated during startup.
var config = new MapperConfiguration(cfg => cfg.CreateMap<Supplier, SupplierInfo>());

var mapper = config.CreateMapper();

SupplierInfo info = mapper.Map<SupplierInfo>(dto);

Console.WriteLine( info.Name );
Console.WriteLine( info.ShortName );
Console.WriteLine( info.BrandName );
}
}

The official Getting Started guide can be found at https://github.com/AutoMapper/AutoMapper/wiki/Getting-started

Is there a pattern for using DTO's without having to duplicate the domain object's properties?

You could use an interface exposing only your object's data, without any domain methods. You'd still need to maintain two classes, but that would be a lot easier since most of the changes could be refactored by your IDE (Eclipse for example). Here's an example:

public interface PersonView {
String getFirstName();
String setFirstName();
}

public void Person implements PersonView {
private String firstName;

@Override // This annotation guarantees the interface is correct
public String getFirstName() {
return firstName;
}

...domain methods...
}

Is not a perfect solution, but it's a pretty clean one.

As for the problem itself, I for one don't really mind exposing the whole object to the view layer. IMHO, I don't think hiding some methods is worth the overhead. The team should have the discipline to use the objects wisely, but that's just my opinion.

JAVA - How to copy attributes of an object to another object having the same attributes?

To expand on my comment:

Using Dozer it can be as easy as:

Mapper mapper = new DozerBeanMapper();
ObjectA source = new ObjectA();
ObjectB target = mapper.map(source , ObjectB.class);

or if your target class doesn't have a no-arg constructor:

ObjectA source = new ObjectA();
ObjectB target = new ObjectB(/*args*/);
mapper.map(source, target );

From the Documentation (emphasis by me):

After performing the Dozer mapping, the result will be a new instance of the destination object that contains values for all fields that have the same field name as the source object. If any of the mapped attributes are of different data types, the Dozer mapping engine will automatically perform data type conversion.

Best Practice (Design Pattern) for copying and augmenting Objects

I think you are looking for Design Pattern Adapter

It's really just wrapping an instance of class A in an instance of class B, to provide a different way of using it / different type.

"I think" because you mention copying issues, so it may not be as much a class/type thing as a persistence / transmission thing.

Depending on your situation you may also be interested in dynamic proxying, but that's a Java feature.

How to efficiently return the max possible integer with a given number of digits

You have just 8 valid answers, so you can hardcode them:

  private static int[] s_Numbers = {
0, 9, 99, 999, 9999, 99999, 999999, 9999999, 99999999};

private static int largestPossibleNumber(int n) {
return s_Numbers[n];
}

Efficient algorithm to get the largest possible number by inserting a single digit

Can't you simplify to something like this:

def get_largest_insertion(long_n,n):
sn=str(abs(long_n))
n=str(n)
sign='-' if long_n<0 else '+'
return(max(int(sign+sn[0:i]+n+sn[i:]) for i in range(len(sn)+1)))


>>> get_largest_insertion(623, 5)
6523
>>> get_largest_insertion(-482,5)
-4582
>>> get_largest_insertion(666,5)
6665

OK, let's make it faster

With simple inspection, I believe it is true that if the number is negative, the insertion will always be one of:

  1. End of the string (such as f(-524242, 8): -5242428); OR
  2. Left of the first larger digit (such as f(-3251342,2): -23251342 or f(-1251342,2): -12251342

And for a positive number:

  1. End of the string (such as f(345342,2): 3453422); OR
  2. Left of the first smaller digit (such as f(3251342,2): 32521342

You can modify my function so that it finds that insertion point in one go with:

def f3(long_n, n):
sn=str(abs(long_n))
sign='-' if long_n<0 else '+'
n=str(n)
if sign=='-':
i=next((i for i,c in enumerate(sn) if c>n), len(sn))
else:
i=next((i for i,c in enumerate(sn) if c<n), len(sn))

return int(sign+sn[0:i]+n+sn[i:])

Now let's benchmark with this:

# your original function is f1
# my original is f2
# new one is f3

import time

# ====================
def attachRest(list_int, str_num, index):
s = [str(i) for i in list_int]
str_num = str_num[index:]
s = s + list(str_num)
num = int("".join(s))
return num

def f1(num, numInsertion):
new_num = []
isNumberInserted = False

if num > 0:
str_num = str(num)
for index, digit in enumerate(str_num):
if numInsertion > int(digit):
new_num.append(numInsertion)
num = attachRest(new_num, str_num, index)
isNumberInserted = True
break
else:
new_num.append(int(digit))

if isNumberInserted is False:
num = num * 10 + numInsertion

return num
else:
str_num = str(-1 * num)
for index, digit in enumerate(str_num):
if numInsertion < int(digit):
new_num.append(numInsertion)
num = attachRest(new_num, str_num, index)
isNumberInserted = True
break
else:
new_num.append(int(digit))

if isNumberInserted is False:
num = num*10 - numInsertion
return num
else:
return -1 * num

# ===============


def f2(long_n,n):
sn=str(abs(long_n))
n=str(n)
sign='-' if long_n<0 else '+'
return(max([int(sign+sn[0:i]+n+sn[i:]) for i in range(len(sn)+1)]))

def f3(long_n, n):
sn=str(abs(long_n))
sign='-' if long_n<0 else '+'
n=str(n)
if sign=='-':
i=next((i for i,c in enumerate(sn) if c>n), len(sn))
else:
i=next((i for i,c in enumerate(sn) if c<n), len(sn))

return int(sign+sn[0:i]+n+sn[i:])

# =====
def cmpthese(funcs, args=(), cnt=100, rate=True, micro=True, deepcopy=True):
from copy import deepcopy
"""Generate a Perl style function benchmark"""
def pprint_table(table):
"""Perl style table output"""
def format_field(field, fmt='{:,.0f}'):
if type(field) is str: return field
if type(field) is tuple: return field[1].format(field[0])
return fmt.format(field)

def get_max_col_w(table, index):
return max([len(format_field(row[index])) for row in table])

col_paddings=[get_max_col_w(table, i) for i in range(len(table[0]))]
for i,row in enumerate(table):
# left col
row_tab=[row[0].ljust(col_paddings[0])]
# rest of the cols
row_tab+=[format_field(row[j]).rjust(col_paddings[j]) for j in range(1,len(row))]
print(' '.join(row_tab))

results={}
for i in range(cnt):
for f in funcs:
if args:
local_args=deepcopy(args)
start=time.perf_counter_ns()
f(*local_args)
stop=time.perf_counter_ns()
results.setdefault(f.__name__, []).append(stop-start)
results={k:float(sum(v))/len(v) for k,v in results.items()}
fastest=sorted(results,key=results.get, reverse=True)
table=[['']]
if rate: table[0].append('rate/sec')
if micro: table[0].append('\u03bcsec/pass')
table[0].extend(fastest)
for e in fastest:
tmp=[e]
if rate:
tmp.append('{:,}'.format(int(round(float(cnt)*1000000.0/results[e]))))

if micro:
tmp.append('{:,.1f}'.format(results[e]/float(cnt)))

for x in fastest:
if x==e: tmp.append('--')
else: tmp.append('{:.1%}'.format((results[x]-results[e])/results[e]))
table.append(tmp)

pprint_table(table)


if __name__=='__main__':
import sys
import time
print(sys.version)

funcs=[f1, f2, f3]

cases=(
(-524242,8),
(345342,2),
(-34734573524242,8),
(71347345345342, 2)
)
for ln, n in cases:
for f in funcs:
print(f'{f.__name__}{ln, n}: {f(ln,n)}')
args=(ln, n)
cmpthese(funcs,args)
print()

That benchmark prints:

3.9.0 (default, Nov 21 2020, 14:55:42) 
[Clang 12.0.0 (clang-1200.0.32.27)]
f1(-524242, 8): -5242428
f2(-524242, 8): -5242428
f3(-524242, 8): -5242428
rate/sec μsec/pass f2 f1 f3
f2 19,777 50.6 -- -37.0% -57.1%
f1 31,402 31.8 58.8% -- -32.0%
f3 46,148 21.7 133.3% 47.0% --

f1(345342, 2): 3453422
f2(345342, 2): 3453422
f3(345342, 2): 3453422
rate/sec μsec/pass f2 f1 f3
f2 19,671 50.8 -- -38.4% -56.5%
f1 31,916 31.3 62.2% -- -29.5%
f3 45,260 22.1 130.1% 41.8% --

f1(-34734573524242, 8): -347345735242428
f2(-34734573524242, 8): -347345735242428
f3(-34734573524242, 8): -347345735242428
rate/sec μsec/pass f2 f1 f3
f2 10,331 96.8 -- -38.1% -72.6%
f1 16,689 59.9 61.5% -- -55.7%
f3 37,679 26.5 264.7% 125.8% --

f1(71347345345342, 2): 721347345345342
f2(71347345345342, 2): 721347345345342
f3(71347345345342, 2): 721347345345342
rate/sec μsec/pass f2 f1 f3
f2 10,579 94.5 -- -67.7% -77.4%
f1 32,779 30.5 209.8% -- -29.9%
f3 46,743 21.4 341.8% 42.6% --

So same approach but inspection vs brute force makes it 2x to 3x faster...



Related Topics



Leave a reply



Submit