Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Java Новый топик    Ответить
 Смена структуры списка  [new]
ahmaroot
Member

Откуда:
Сообщений: 6
Всем доброго дня.
Имеется коллеция объектов List<Order>:
[{"type": "BUY", "price": 2132.90, "count": 123},{"type": "SELL", "price": 90.12, "count": 2}, {"type": "BUY", "price": 435, "count": 4}, {"type": "SELL", "price": 32.56, "count": 1}, {"type": "SELL", "price": 77.8, "count": 65}]


Необходимо сделать мапу такого формата:
[{"BUY": {123:2132.90, 4:435}},{"SELL": {2: 90.12, 1:32.56, 65:77.8}}]


В идеале конечно же хотелось бы за одну итерацию. Я смотрю в сторону mapName.stream().collect, но пока попытки тщетны. Может кто-то сталкивался с таким?
28 окт 20, 10:38    [22221978]     Ответить | Цитировать Сообщить модератору
 Re: Смена структуры списка  [new]
PetroNotC Sharp
Member

Откуда:
Сообщений: 6525
ahmaroot
попытки тщетны
покаж
28 окт 20, 10:58    [22222000]     Ответить | Цитировать Сообщить модератору
 Re: Смена структуры списка  [new]
mad_nazgul
Member

Откуда:
Сообщений: 5596
ahmaroot,

Не истины ради, а флейма для.

Код на Kotlin'е
<:o)

data class Order(
    val type: String,
    val price: Double,
    val count: Int
)

val list = listOf(
    Order("BUY", 2132.90, 123),
    Order("SELL", 90.12, 2),
    Order("BUY", 435.0, 4),
    Order("SELL", 32.56, 1),
    Order("SELL", 77.8, 65)
)

fun main(args: Array<String>) {

    val result =
        list
            .groupingBy { it.type }
            .aggregate {key, accumulator: MutableMap<Int, Double>?, element, first ->
                        if(first) {
                            mutableMapOf(element.count to element.price)
                        } else {
                            accumulator?.apply {
                                put(element.count, element.price)
                            }
                        }
            }

    println(result)
}
28 окт 20, 15:53    [22222385]     Ответить | Цитировать Сообщить модератору
 Re: Смена структуры списка  [new]
забыл ник
Member

Откуда:
Сообщений: 3436
Ну и на Scala -
import cats.implicits._

orders.groupBy(_.type).mapValues(combine)


Только нафига это тут?
28 окт 20, 17:11    [22222448]     Ответить | Цитировать Сообщить модератору
 Re: Смена структуры списка  [new]
ahmaroot
Member

Откуда:
Сообщений: 6
Друзья, прошу прощения. Я немного не так сформулировал описание.
Изначально список объетов такой:
[{"priceSell": 2132.90, "priceBuy": 3, "count": 123},{"priceSell": 90.12, "priceBuy": 3, "count": 2}, {"priceSell": 39, "priceBuy": 5, "count": 4}]


И на выходе нужно мапу формата вида:
[{"BUY": {count:priceBuy}},{"SELL": {count:priceSell}}]



Я пока сделал так:
Map<String, Map<String, Double>> response = new HashMap<>() {{
            put("BUY", orders.stream().collect(
                    Collectors.toMap(Order::getCount, Order::getBuyPrice)
            ));
            put("SELL", orders.stream().collect(
                    Collectors.toMap(Order::getCount, Order::getSellPrice)
            ));
        }};


Оно работает. Но минусов много - как минимум 2 раза бежим по одной коллекции. Ну и может как-то это можно оптимизировать и сделать по другому? Спасибо!
28 окт 20, 18:42    [22222543]     Ответить | Цитировать Сообщить модератору
 Re: Смена структуры списка  [new]
mayton
Member

Откуда: loopback
Сообщений: 49768
ahmaroot

Оно работает. Но минусов много - как минимум 2 раза бежим по одной коллекции. Ну и может как-то это можно оптимизировать и сделать по другому? Спасибо!

Ты какую вообще задачу поставил? Просто решить группировку - это одно. Решить в один проход - это другое.
Решить на Котлин или на Scala или решить с использованием stream это третье. Всё сразу - не будет.

Во всех случаях мы превращаемся в слух и пытаемся понять что-же на самом деле хочет автор
и какого еще птичьего молока ему надо. Вот тебе молоко пингвинов. Вот - страусов. Бери.
28 окт 20, 19:49    [22222596]     Ответить | Цитировать Сообщить модератору
 Re: Смена структуры списка  [new]
ahmaroot
Member

Откуда:
Сообщений: 6
ahmaroot
Друзья, прошу прощения. Я немного не так сформулировал описание.
Изначально список объетов такой:
[{"priceSell": 2132.90, "priceBuy": 3, "count": 123},{"priceSell": 90.12, "priceBuy": 3, "count": 2}, {"priceSell": 39, "priceBuy": 5, "count": 4}]


И на выходе нужно мапу формата вида:
[{"BUY": {count:priceBuy}},{"SELL": {count:priceSell}}]



Я пока сделал так:
Map<String, Map<String, Double>> response = new HashMap<>() {{
            put("BUY", orders.stream().collect(
                    Collectors.toMap(Order::getCount, Order::getBuyPrice)
            ));
            put("SELL", orders.stream().collect(
                    Collectors.toMap(Order::getCount, Order::getSellPrice)
            ));
        }};


Оно работает. Но минусов много - как минимум 2 раза бежим по одной коллекции. Ну и может как-то это можно оптимизировать и сделать по другому? Спасибо!


@mayton
Как я понимаю, это н совсем группировка ведь, разве нет? В идеале, повторюсь, хочется обойтись за 1 раз коллекцию. Формат входных и выходных данных, думаю, ясен.
28 окт 20, 20:46    [22222621]     Ответить | Цитировать Сообщить модератору
 Re: Смена структуры списка  [new]
mayton
Member

Откуда: loopback
Сообщений: 49768
ahmaroot, собеседование что-ли проходишь?

Тогда попробуй напиши эту функцию через обычный цикл на Pure-Java. В 1 проход.
Потом мы попробуем его привести к форме работы со стримами. Но так хоть
будет некая устойчивая точка которая работает. Надо будет написать свою
функцию для reduce или collect.
28 окт 20, 20:58    [22222624]     Ответить | Цитировать Сообщить модератору
 Re: Смена структуры списка  [new]
забыл ник
Member

Откуда:
Сообщений: 3436
ahmaroot
ahmaroot
Друзья, прошу прощения. Я немного не так сформулировал описание.
Изначально список объетов такой:
[{"priceSell": 2132.90, "priceBuy": 3, "count": 123},{"priceSell": 90.12, "priceBuy": 3, "count": 2}, {"priceSell": 39, "priceBuy": 5, "count": 4}]


И на выходе нужно мапу формата вида:
[{"BUY": {count:priceBuy}},{"SELL": {count:priceSell}}]



Я пока сделал так:
Map<String, Map<String, Double>> response = new HashMap<>() {{
            put("BUY", orders.stream().collect(
                    Collectors.toMap(Order::getCount, Order::getBuyPrice)
            ));
            put("SELL", orders.stream().collect(
                    Collectors.toMap(Order::getCount, Order::getSellPrice)
            ));
        }};


Оно работает. Но минусов много - как минимум 2 раза бежим по одной коллекции. Ну и может как-то это можно оптимизировать и сделать по другому? Спасибо!


@mayton
Как я понимаю, это н совсем группировка ведь, разве нет? В идеале, повторюсь, хочется обойтись за 1 раз коллекцию. Формат входных и выходных данных, думаю, ясен.

В такой постановке это не группировка. Это итерация со стейтом(аккумулирование).
Исторически самым первым решением такой задачи являлось внешнее итерирование в цикле for
int sell = 0
int buy = 0
for(order: orders){
  sell += order.getSell
  buy += order.getBuy
}
map = new HashMap
map.put("SELL", sell)
map.put("BUY", buy)



На смену ему пришли стримы, которые содержат логику итерирования внутри себя, что есть шаг вперед

int sell = 0;
int buy = 0;
orders.stream().foreach(o => {sell += o.getSell, buy += o.getBuy})
map = new HashMap
map.put("SELL", sell)
map.put("BUY", buy)


Но так как переменные sell и buy у нас мутабельные и могут утечь, усложняем концепцию дальше. Так как твоя операция на математическом языке ассоциативная и коммутативная то для нее всего то и надо что начальное значение и операция, которая должна выполниться над каждым элементом. Вторая часть уже есть, осталось дело за первой. Для этого введем класс-аккумулятор, который будет иммутабельный и на каждом проходе будет создаваться его модифицированная копия. Также стоит отметить что объект класса создается прямо в методе reduce и никуда утечь не может.

public class Accum(sell int, buy int){
  public int getSell() = {return sell}
  public int getBuy() = {return buy}
}

либо с использованием Lombok - 
@Data
public class Accum(sell int, buy int)

либо в новой java 
record Accum(sell int, buy int)

Accum result = orders.stream.reduce(new Accum(0,0), (order, accum) => new Accum(accum.getBuy + order.getBuy, accum.getSell + order.getSell))
map = new HashMap
map.put("SELL", result.getSell)
map.put("BUY", result.getBuy)


В теории можно обойтись и без промежуточного класса и объекта accum, но код будет выглядеть громоздким(на Java)

public Map initMap() = {
map = new HashMap
map.put("SELL", 0)
map.put("BUY", 0)
return map
}

orders.stream.reduce(initMap(), (map, order) => { map.put("BUY", map.get("BUY") + order.getBuy), map.put("SELL", map.get("SELL") + order.getSell) }


Еще больше можно абстрагировать логику вводя математическое понятие Monoid, это такая алгебраическая структура, которая имеет начальное значение некого типа и бинарную ассоциативную операцию, результатом которой будет элемент того же типа что и начальное значение. Это позволяет еще более абстрагировать и переиспользовать код, но это уже далеко за рамками ООП и java-новичка.
28 окт 20, 21:39    [22222637]     Ответить | Цитировать Сообщить модератору
 Re: Смена структуры списка  [new]
mayton
Member

Откуда: loopback
Сообщений: 49768
Не дай бог еще собеседующий будет знать что такое моноиды, и абелевы группы и скажет - Ану-ка давай ка поговорим про это...
28 окт 20, 23:10    [22222672]     Ответить | Цитировать Сообщить модератору
 Re: Смена структуры списка  [new]
mad_nazgul
Member

Откуда:
Сообщений: 5596
забыл ник
[
Еще больше можно абстрагировать логику вводя математическое понятие Monoid, это такая алгебраическая структура, которая имеет начальное значение некого типа и бинарную ассоциативную операцию, результатом которой будет элемент того же типа что и начальное значение. Это позволяет еще более абстрагировать и переиспользовать код, но это уже далеко за рамками ООП и java-новичка.


У вас по дороге цена потерялась :-)

data class Order(
    val type: String,
    val price: Double,
    val count: Int
)

val list = listOf(
    Order("BUY", 2132.90, 123),
    Order("SELL", 90.12, 2),
    Order("BUY", 435.0, 4),
    Order("SELL", 32.56, 1),
    Order("SELL", 77.8, 65)
)

fun main(args: Array<String>) {

    val result =
        list
            .groupingBy { it.type }
            .aggregate {key, accumulator: Pair<Int, Double>?, element, first ->
                        if(first) {
                            element.count to element.price
                        } else {
                            (accumulator?.first?:0 + element.count) to (accumulator?.second?:0.0 + element.price)
                        }
            }

    println(result)
}
29 окт 20, 05:43    [22222723]     Ответить | Цитировать Сообщить модератору
 Re: Смена структуры списка  [new]
mad_nazgul
Member

Откуда:
Сообщений: 5596
mad_nazgul,

Прошу прощения. Одно сравнение лишнее.

data class Order(
    val type: String,
    val price: Double,
    val count: Int
)

val list = listOf(
    Order("BUY", 2132.90, 123),
    Order("SELL", 90.12, 2),
    Order("BUY", 435.0, 4),
    Order("SELL", 32.56, 1),
    Order("SELL", 77.8, 65)
)

fun main(args: Array<String>) {

    val result =
        list
            .groupingBy { it.type }
            .aggregate {key, accumulator: Pair<Int, Double>?, element, first ->
                        (accumulator?.first?:0 + element.count) to (accumulator?.second?:0.0 + element.price)
            }

    println(result)
}
29 окт 20, 05:45    [22222724]     Ответить | Цитировать Сообщить модератору
Все форумы / Java Ответить