Как собрать свойства List <Map>по уникальному свойству, используя MultiMap? - PullRequest
0 голосов
/ 21 ноября 2018

У меня есть список рассказов.Используя уникальный property(id), я хочу собрать ключевое слово и таргетинг в виде списка значений.Могу ли я сделать это с MultiMap?Или есть другая библиотека для этого?

[{
    id = 1,
    title = Onboarding,
    keyword = new joinee,
    targeting = finance
}, {
    id = 1,
    title = Onboarding,
    keyword = training,
    targeting = HR
}]

Требуемый вывод должен выглядеть следующим образом:

{
    id = 1,
    title = Onboarding,
    keyword = [new joinee,training], //may be keywords - plural
    targeting = [HR,finance]
} 

Пример моего проверенного кода выглядит следующим образом:

package prac;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class JavaPrac {
    public static void main(String[] args) {
        Multimap<Integer, Map> multiMap = ArrayListMultimap.create();

        List<Map> stories=new ArrayList();

        Map story1=new HashMap();
        story1.put("id", 1);
        story1.put("title", "Onboarding");
        story1.put("keyword","new joinee");
        story1.put("targeting","finance");

        Map story2=new HashMap();
        story2.put("id", 1);
        story2.put("title", "Onboarding");
        story2.put("keyword","training");
        story2.put("targeting","HR");

        stories.add(story1);
        stories.add(story2);

        System.out.println(stories);

        stories.forEach((story) -> {
            multiMap.put((Integer) story.get("id"), story);
        });
    }
}

Ответы [ 3 ]

0 голосов
/ 21 ноября 2018

Да, вы можете сделать это с Multimap.Во-первых, я бы определил pojo для Story, чтобы прояснить ситуацию:

public class Story {
    private int id;
    private String title;
    private String keyword;
    private String targeting;
//getters setters
}

Во-вторых, вам нужно определить ключ с хеш-кодом и равен.

public static class StoryKey {
    private final int id;
    private final String title;

    public StoryKey(int id, String title) {
        this.id = id;
        this.title = title;
    }
//getters

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        StoryKey storyKey = (StoryKey) o;

        if (id != storyKey.id) return false;
        return title != null ? title.equals(storyKey.title) : storyKey.title == null;
    }

    @Override
    public int hashCode() {
        int result = id;
        result = 31 * result + (title != null ? title.hashCode() : 0);
        return result;
    }

Код будет выглядеть следующим образом:

  ArrayListMultimap<StoryKey, Story> multiMap = ArrayListMultimap.create();

        List<Story> stories = new ArrayList();
        Story story1 = new Story();

        story1.setId(1);
        story1.setTitle("Onboarding");
        story1.setKeyword("training");
        story1.setTargeting("HR");

        Story story2 = new Story();

        story2.setId(1);
        story2.setTitle("Onboarding");
        story2.setKeyword("new joinee,");
        story2.setTargeting("finance");

        stories.add(story1);
        stories.add(story2);

        System.out.println(stories);


        stories.
                forEach((story) -> {
                    multiMap.put(new StoryKey(story.getId(), story.getTitle()), story);
                });

        multiMap.keys().forEach(key ->
                System.out.println(
                        "id =" + key.getId() +
                        " title =" + key.getTitle()+
                        "keyword =" + multiMap.get(key).stream().map(story->story.getKeyword()).collect(Collectors.toList()).toString()+
                        "targeting ="+ multiMap.get(key).stream().map(story->story.getTargeting()).collect(Collectors.toList()).toString())
     );
0 голосов
/ 21 ноября 2018

Вот решение, использующее классы для представления истории и тегов:

public static void main(String[] args) {
    TagsCollector app = new TagsCollector();
    app.go();
}

private void go() {
    List<Story> stories = createStories();
    System.out.println(stories);
    Map<Long, Tags> tagsById = collectTags(stories);
    tagsById.forEach((aLong, tags) -> System.out.println(tags));
}

private List<Story> createStories() {
    return Arrays.asList(
            new Story(1, "Onboarding", "new joinee", "finance"),
            new Story(1, "Onboarding", "training", "HR")
    );
}

private Map<Long, Tags> collectTags(List<Story> stories) {
    Map<Long, Tags> tagsById = new HashMap<>();
    stories.forEach(s -> {
        Tags tags = tagsById.computeIfAbsent(s.id, v -> new Tags(s));
        tags.getKeywords().add(s.getKeyword());
        tags.getTargetings().add(s.getTargeting());
    });
    return tagsById;
}

Класс, используемый для представления истории:

public class Story {

    private final long id;
    private final String title;
    private final String keyword;
    private final String targeting;

    public Story(long id, String title, String keyword, String targeting) {
        this.id = id;
        this.title = title;
        this.keyword = keyword;
        this.targeting = targeting;
    }

    public long getId() {
        return id;
    }

    public String getTitle() {
        return title;
    }

    public String getKeyword() {
        return keyword;
    }

    public String getTargeting() {
        return targeting;
    }

    @Override
    public String toString() {
        return String.format("Story %s, title=%s, keyword=%s, targeting=%s", id, title, keyword, targeting);
    }
}

Класс, используемый для представления тегов:

public class Tags {
    private final long id;
    private final String title;
    private final List<String> keywords = new ArrayList<>();
    private final List<String> targetings = new ArrayList<>();

    Tags(Story story) {
        this.id = story.id;
        this.title = story.title;
    }

    public List<String> getKeywords() {
        return keywords;
    }

    public List<String> getTargetings() {
        return targetings;
    }

    @Override
    public String toString() {
        return String.format("Tags for id %s, title:%s: keywords=%s, targetings=%s", id, title, keywords, targetings);
    }
}

Выход

[Story 1, title=Onboarding, keyword=new joinee, targeting=finance, Story 1, title=Onboarding, keyword=training, targeting=HR]
Tags for id 1, title:Onboarding: keywords=[new joinee, training], targetings=[finance, HR]
0 голосов
/ 21 ноября 2018

Мультикарта может хранить только несколько значений на ключ, но вам нужно объединить эти несколько значений, чтобы получить один элемент с одинаковыми идентификатором и заголовком, а также набор ключевых слов и информации о таргетинге.Поэтому, вероятно, было бы лучше иметь что-то вроде MultiStory или уже иметь Story, содержащее эти коллекции.

Я бы предложил использовать правильные объекты вместо карт, но с картами и лямбдами Java 8 вы можете использовать compute() и т. Д. Для создания карт, содержащих коллекции, и комбинировать карты, которые этого не делают.

Вот пример того, как вы делаете это с картами.Обратите внимание, что это очень плохой стиль, и пример с использованием правильных pojos будет следующим:

Отказ от ответственности: пример, основанный на коде OP, не рекомендуется (см. Текст выше)

//Problem 1: we don't know the type of the values, i.e. we could put anything for "id" etc.
Map<String, Object> story1=new HashMap<>();
story1.put("id", 1);
story1.put("title", "Onboarding");
story1.put("keyword","new joinee");
story1.put("targeting","finance");

Map<String, Object> story2=new HashMap<>();
story2.put("id", 1);
story2.put("title", "Onboarding");
story2.put("keyword","training");
story2.put("targeting","HR");

List<Map<String, Object>> stories=new ArrayList<>();

stories.add(story1);
stories.add(story2);

Map<Integer, Map<String, Object>> combined = new HashMap<>();

stories.forEach((story) -> {
  //Problem 2: because we don't know the type of the values we need a lot of nasty casts
  Map<String, Object> combinedStory = combined.computeIfAbsent( (Integer)story.get( "id" ), k -> new HashMap<String, Object>() );
  combinedStory.put("id", story.get( "id" ) );
  combinedStory.put("title", story.get( "title" ) );

  //Problem 3: the combined map would look a lot like your "story" maps but would contain different types
  ((List<String>)combinedStory.computeIfAbsent( "keyword", v -> new List<String>() )).add( (String)story.get("keyword") );
  ((List<String>)combinedStory.computeIfAbsent( "targeting", v -> new List<String>() )).add( (String)story.get("targeting") );
});

Использование POJO

Вот очень упрощенный пример того, как вы делаете это с правильными объектами Java (POJO).Обратите внимание, что они должны максимально напоминать ваш код, и есть много других проблем, но здесь их будет слишком много, и лучше разработанный код будет намного больше и, вероятно, труднее для понимания - в конце концов, это просто означаетчтобы показать вам разницу.

Сначала давайте определим наши классы (для простоты я сделал поля общедоступными, обычно вы этого не сделаете):

class Story {
  public final int id;
  public String title;
  public String keyword;
  public String targeting;

  public Story(int storyId) {
    id = storyId ;
  }
}

class MultiStory {
  public final int id;
  public String title;
  public Set<String> keywords = new HashSet<>();
  public Set<String> targetingInfo = new HashSet<>();

  public MultiStory( int storyId ) {
    id = storyId ;
  }
}

Затем давайте повторим код выше:

Story story1=new Story( 1 );
story1.title = "Onboarding";
story1.keyword = "new joinee";
story1.targeting = "finance";

Story story2=new Story( 1 );
story2.title = "Onboarding";
story2.keyword = "training";
story2.targeting = "HR";

List<Story> stories=new ArrayList<>();

stories.add(story1);
stories.add(story2);

Map<Integer, MultiStory> combined = new HashMap<>();

stories.forEach((story) -> {
  MultiStory multiStory = combined.computeIfAbsent( story.id, v -> new MultiStory( story.id ) );
  multiStory.title = story.title;
  multiStory.keywords.add( story.keyword );
  multiStory.targetingInfo.add( story.targeting );
});

Как видите, нет необходимости в преобразованиях, и ясно, какие поля доступны (хотя и не обязательно заполнены), что упрощает анализ кода и обнаружение ошибок (компилятор может помочьмного чего здесь не получилось в примере, использующем карты).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...