Skip to content

Instantly share code, notes, and snippets.

@seolys
Last active August 17, 2023 02:47
Show Gist options
  • Save seolys/a2af9400b94f777410906ffcbc286a50 to your computer and use it in GitHub Desktop.
Save seolys/a2af9400b94f777410906ffcbc286a50 to your computer and use it in GitHub Desktop.
DistributedAmountMap
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public abstract class DistributedAmountMap<T> {
protected abstract Long getId(T item);
protected final Map<Long, BigDecimal> getMap(final List<T> list, final BigDecimal distributeAmount) {
var remainingAmount = distributeAmount;
final var distributedAmountMap = new HashMap<Long, BigDecimal>();
final var totalCount = BigDecimal.valueOf(list.size());
for (final var instance : list) {
final var id = getId(instance);
// 각 항목에 할당될 금액 계산
final var distributedAmount = distributeAmount.divide(totalCount, 0, RoundingMode.DOWN);
distributedAmountMap.put(id, distributedAmount);
remainingAmount = remainingAmount.subtract(distributedAmount);
}
if (!list.isEmpty()) {
final Long firstId = getId(list.get(0));
final BigDecimal firstDistributedAmount = distributedAmountMap.get(firstId);
distributedAmountMap.put(firstId, firstDistributedAmount.add(remainingAmount));
}
return distributedAmountMap;
}
}
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
public class DistributedAmountMapTest {
private DistributedAmountMap<DummyItem> distributedAmountMap;
@BeforeEach
public void setUp() {
distributedAmountMap = new DistributedAmountMap<DummyItem>() {
@Override
protected Long getId(final DummyItem item) {
return item.getId();
}
};
}
@Test
void test1() {
final List<DummyItem> itemList = List.of(
new DummyItem(1L, new BigDecimal("100.00")),
new DummyItem(2L, new BigDecimal("100.00")),
new DummyItem(3L, new BigDecimal("100.00"))
);
final BigDecimal distributeAmount = new BigDecimal("1000");
final Map<Long, BigDecimal> resultMap = distributedAmountMap.getMap(itemList, distributeAmount);
assertThat(resultMap)
.containsEntry(1L, new BigDecimal("334"))
.containsEntry(2L, new BigDecimal("333"))
.containsEntry(3L, new BigDecimal("333"))
.hasSize(3);
}
@Test
void test2() {
final List<DummyItem> itemList = List.of(
new DummyItem(1L, new BigDecimal("100.00")),
new DummyItem(2L, new BigDecimal("150.00")),
new DummyItem(3L, new BigDecimal("200.00"))
);
final BigDecimal distributeAmount = new BigDecimal("450");
final Map<Long, BigDecimal> resultMap = distributedAmountMap.getMap(itemList, distributeAmount);
assertThat(resultMap)
.containsEntry(1L, new BigDecimal("150"))
.containsEntry(2L, new BigDecimal("150"))
.containsEntry(3L, new BigDecimal("150"))
.hasSize(3);
}
// DummyItem 클래스 정의
private static class DummyItem {
private final Long id;
private final BigDecimal amount;
public DummyItem(final Long id, final BigDecimal amount) {
this.id = id;
this.amount = amount;
}
public Long getId() {
return id;
}
public BigDecimal getAmount() {
return amount;
}
}
}
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public abstract class RateDistributedAmountMap<T> {
protected final Map<Long, BigDecimal> getMap(final List<T> list, final BigDecimal distributeAmount) {
final var totalDistributionRateBaseAmount = getTotalDistributionRateBaseAmount(list);
final var rateMap = getRateMap(list, totalDistributionRateBaseAmount);
return getDistributeMap(list, distributeAmount, rateMap);
}
private BigDecimal getTotalDistributionRateBaseAmount(final List<T> list) {
return list.stream()
.map(this::getDistributionRateBaseAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
protected abstract Long getId(T item);
protected abstract BigDecimal getDistributionRateBaseAmount(T item);
private Map<Long, BigDecimal> getDistributeMap(
final List<T> list,
final BigDecimal distributeAmount,
final HashMap<Long, BigDecimal> rateMap
) {
var remainingAmount = distributeAmount;
final var distributedAmountMap = new HashMap<Long, BigDecimal>();
for (final var instance : list) {
final var id = getId(instance);
final var rate = rateMap.get(id);
final var distributedAmount = distributeAmount.multiply(rate).setScale(0, RoundingMode.DOWN);
distributedAmountMap.put(id, distributedAmount);
remainingAmount = remainingAmount.subtract(distributedAmount);
}
if (!list.isEmpty()) {
final Long firstId = getId(list.get(0));
final BigDecimal firstDistributedAmount = distributedAmountMap.get(firstId);
distributedAmountMap.put(firstId, firstDistributedAmount.add(remainingAmount));
}
return distributedAmountMap;
}
private HashMap<Long, BigDecimal> getRateMap(final List<T> list, final BigDecimal totalDistributionRateBaseAmount) {
final var rateMap = new HashMap<Long, BigDecimal>();
for (final var instance : list) {
final var id = getId(instance);
final var amount = getDistributionRateBaseAmount(instance);
final BigDecimal rate = amount.divide(totalDistributionRateBaseAmount, 16, RoundingMode.HALF_UP);
rateMap.put(id, rate);
}
return rateMap;
}
}
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public interface RateDistributedAmountMapFactory<T> {
Long getId(T item);
Money getDistributionRateBaseAmount(T item);
default Map<Long, Money> createMap(final List<T> list, final Money totalDistributeAmount) {
final var totalDistributionRateBaseAmount = getTotalDistributionRateBaseAmount(list);
final var rateMap = getRateMap(list, totalDistributionRateBaseAmount);
return getDistributeMap(list, totalDistributeAmount, rateMap);
}
private Money getTotalDistributionRateBaseAmount(final List<T> list) {
return list.stream()
.map(this::getDistributionRateBaseAmount)
.reduce(Money.ZERO, Money::add);
}
private Map<Long, Money> getDistributeMap(
final List<T> list,
final Money distributeAmount,
final HashMap<Long, BigDecimal> rateMap
) {
var remainingAmount = distributeAmount;
final var distributedAmountMap = new HashMap<Long, Money>();
for (final var instance : list) {
final var id = getId(instance);
final var rate = rateMap.get(id);
final var distributedAmount = distributeAmount.multiply(rate)
.setScale(0, RoundingMode.DOWN);
distributedAmountMap.put(id, distributedAmount);
remainingAmount = remainingAmount.subtract(distributedAmount);
}
if (!list.isEmpty()) {
final Long firstId = getId(list.get(0));
final var firstDistributedAmount = distributedAmountMap.get(firstId);
distributedAmountMap.put(firstId, firstDistributedAmount.add(remainingAmount));
}
return distributedAmountMap;
}
private HashMap<Long, BigDecimal> getRateMap(
final List<T> list,
final Money totalDistributionRateBaseAmount
) {
final var rateMap = new HashMap<Long, BigDecimal>();
for (final var instance : list) {
final var id = getId(instance);
final var amount = getDistributionRateBaseAmount(instance);
final var rate = amount.getValue()
.divide(totalDistributionRateBaseAmount.getValue(), 16, RoundingMode.HALF_EVEN);
rateMap.put(id, rate);
}
return rateMap;
}
}
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
public class RateDistributedAmountMapTest {
private RateDistributedAmountMap<DummyItem> rateDistributedAmountMap;
@BeforeEach
public void setUp() {
rateDistributedAmountMap = new RateDistributedAmountMap<DummyItem>() {
@Override
protected Long getId(final DummyItem item) {
return item.getId();
}
@Override
protected BigDecimal getDistributionRateBaseAmount(final DummyItem item) {
return item.getAmount();
}
};
}
@Test
public void test1() {
final List<DummyItem> itemList = List.of(
new DummyItem(1L, new BigDecimal("100.00")),
new DummyItem(2L, new BigDecimal("100.00")),
new DummyItem(3L, new BigDecimal("100.00"))
);
final BigDecimal distributeAmount = new BigDecimal("1000");
final Map<Long, BigDecimal> resultMap = rateDistributedAmountMap.getMap(itemList, distributeAmount);
assertThat(resultMap)
.containsEntry(1L, new BigDecimal("334"))
.containsEntry(2L, new BigDecimal("333"))
.containsEntry(3L, new BigDecimal("333"))
.hasSize(3);
}
@Test
public void test2() {
final List<DummyItem> itemList = List.of(
new DummyItem(1L, new BigDecimal("100.00")),
new DummyItem(2L, new BigDecimal("150.00")),
new DummyItem(3L, new BigDecimal("200.00"))
);
final BigDecimal distributeAmount = new BigDecimal("450.00");
final Map<Long, BigDecimal> resultMap = rateDistributedAmountMap.getMap(itemList, distributeAmount);
assertThat(resultMap)
.containsEntry(1L, new BigDecimal("100"))
.containsEntry(2L, new BigDecimal("150"))
.containsEntry(3L, new BigDecimal("200"))
.hasSize(3);
}
private static class DummyItem {
private final Long id;
private final BigDecimal amount;
public DummyItem(final Long id, final BigDecimal amount) {
this.id = id;
this.amount = amount;
}
public Long getId() {
return id;
}
public BigDecimal getAmount() {
return amount;
}
}
}
import static org.assertj.core.api.Assertions.assertThat;
import java.util.List;
import kr.co._29cm.claim.v2.domain.common.Money;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class UnitDistributedAmountMapFactoryTest {
private UnitDistributedAmountMapFactory factory;
@BeforeEach
public void setup() {
factory = new UnitDistributedAmountMapFactory();
}
@Test
public void 단일_아이템_분배_금액_0일때_맵_생성시_0으로_분배() {
// given
final List<DummyItem> itemList = List.of(
new DummyItem(1L, Money.of(100))
);
final Money totalDistributeAmount = Money.of(0);
// when
final var distributedMap = factory.createMap(itemList, DummyItem::getId,
totalDistributeAmount);
// then
assertThat(distributedMap)
.containsEntry(1L, Money.ZERO)
.hasSize(1);
}
@Test
public void 단일_아이템_분배_금액_0이_아닐때_맵_생성시_단일_아이템에_분배() {
// given
final List<DummyItem> itemList = List.of(
new DummyItem(1L, Money.of(100))
);
final Money totalDistributeAmount = Money.of(1000);
// when
final var distributedMap = factory.createMap(itemList, DummyItem::getId,
totalDistributeAmount);
// then
assertThat(distributedMap)
.containsEntry(1L, Money.of(1000))
.hasSize(1);
}
@Test
public void 여러_아이템_분배_금액_균등할때_맵_생성시_균등하게_분배() {
// given
final List<DummyItem> itemList = List.of(
new DummyItem(1L, Money.of(100)),
new DummyItem(2L, Money.of(150)),
new DummyItem(3L, Money.of(200))
);
final Money totalDistributeAmount = Money.of(450);
// when
final var distributedMap = factory.createMap(itemList, DummyItem::getId,
totalDistributeAmount);
// then
assertThat(distributedMap)
.containsEntry(1L, Money.of(150))
.containsEntry(2L, Money.of(150))
.containsEntry(3L, Money.of(150))
.hasSize(3);
}
@Test
public void 여러_아이템_분배_금액_불균등할때_맵_생성시_비례하여_분배() {
// given
final List<DummyItem> itemList = List.of(
new DummyItem(1L, Money.of(100)),
new DummyItem(2L, Money.of(100)),
new DummyItem(3L, Money.of(100))
);
final Money totalDistributeAmount = Money.of(1000);
// when
final var distributedMap = factory.createMap(itemList, DummyItem::getId,
totalDistributeAmount);
// then
assertThat(distributedMap)
.containsEntry(1L, Money.of(334))
.containsEntry(2L, Money.of(333))
.containsEntry(3L, Money.of(333))
.hasSize(3);
}
@Getter
@AllArgsConstructor
private static class DummyItem {
private final Long id;
private final Money amount;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment