如何查找重复的数组列表对象,并将唯一键的几个字段合并。

huangapple go评论76阅读模式
英文:

How to find duplicate array list object and merge few fields with the unique key

问题

Person elpidio = new Person.Builder().id(3L).firstName("elpidio").secondName("gomez").ext("121").build();

Person romual1 = new Person.Builder().id(4L).firstName("romualdo").secondName("perez").ext("141").build();

Person romual2 = new Person.Builder().id(4L).firstName("romualdo").secondName("perez").ext("144").build();

List<Person> personList = Arrays.asList(elpidio, romual1, romual2);

Map<String, List<Person>> groupedByFullName = personList.stream()
        .collect(Collectors.groupingBy(
                person -> person.getFirstName() + " " + person.getSecondName()));

List<Person> mergedPersons = groupedByFullName.values().stream()
        .map(persons -> {
            Person.Builder builder = new Person.Builder();
            builder.firstName(persons.get(0).getFirstName());
            builder.secondName(persons.get(0).getSecondName());
            List<String> extValues = persons.stream().map(Person::getExt).collect(Collectors.toList());
            builder.ext(extValues);
            return builder.build();
        })
        .collect(Collectors.toList());

System.out.println(mergedPersons);

Output:

[Person [firstName=elpidio, secondName=gomez, ext=[121]],
Person [firstName=romualdo, secondName=perez, ext=[141, 144]]]

Note: The provided code assumes you have a Person class with appropriate methods like getFirstName(), getSecondName(), getExt() and a Builder class with appropriate methods. Make sure to adjust the code to your actual class structure.

英文:
Person elpidio = new Person.Builder().id(3L).firstName(&quot;elpidio&quot;).secondName(&quot;gomez&quot;).ext(&quot;121&quot;).build();
Person romual1 = new Person.Builder().id(4L).firstName(&quot;romualdo&quot;).secondName(&quot;perez&quot;).ext(&quot;141&quot;).build();
Person romual2 = new Person.Builder().id(4L).firstName(&quot;romualdo&quot;).secondName(&quot;perez&quot;).ext(&quot;144&quot;).build();

Now I need a out put some thing like this.

[Person [firstName=elpidio, secondName=gomez,ext=[121]],
Person [firstName=romualdo, secondName=perez,ext=[121,144]]]

I refereed to below ans. But the problem I found is that I have many property in Person, and out of that I just need to merge one fields. So the entry set is giving issue. Any suggestion in welcome.

https://stackoverflow.com/questions/53164975/extract-duplicate-objects-from-a-list-in-java-8

https://stackoverflow.com/questions/57221600/how-to-merge-child-objects-in-list-based-on-duplicate-parent-object

答案1

得分: 2

这只是一种方法(可能是一个过于复杂、不太好的方式,我还没有完全检查所有内容)。我喜欢这个方法,因为它展示了如何创建一个自定义的收集器,这对于理解底层原理非常有用。希望注释足够解释清楚。如果不够的话,有很多关于自定义收集器的指南(比如这个)。

Person elpidio = Person.Builder().id(3L).firstName("elpidio").secondName("gomez").ext("121").build();
Person romual1 = Person.Builder().id(4L).firstName("romualdo").secondName("perez").ext("141").build();
Person romual2 = Person.Builder().id(4L).firstName("romualdo").secondName("perez").ext("144").build();
Map<Long, Person> filteredPersons = Stream.of(elpidio, romual1, romual2).collect(Collector.of(
        HashMap::new,  // 从空列表开始
        (result, entry) -> {
            if (result.containsKey(entry.id)) {
                // 如果我们的映射中已经有这个 id,我们将 ext 添加到结果列表中。这是基于 Person 中的 "ext" 可能是某种集合(因为我不想去创建一个新的任意类——换句话说,这个练习留给读者完成 =) 如果你不想要 ext 的重复值,确保它是一个集合或者其他可以移除重复值的数据结构。
                result.get(entry.id).ext.addAll(entry.ext);
            } else {
                result.put(entry.id, entry);
            }
        },
        (result1, result2) -> {
            // 这处理了并行处理收集的情况。我们如何合并两个结果映射呢?
            // 在这个情况下,我们随意地称呼 "result1" 为要检查的版本,但实际上并不重要。
            for (Map.Entry<Long, Person> person : result2.entrySet()) {
                if (result1.containsKey(person.getKey())) {
                    result1.get(person.getKey()).ext.addAll(person.getValue().ext);
                } else {
                    result1.put(person.getKey(), person.getValue());
                }
            }
            return result1;
        }));
filteredPersons.forEach((k, v) -> System.out.println(k + " - first: " + v.first + " second: " + v.second + " " +
        "ext: " + v.ext));

输出:

3 - first: elpidio second: gomez ext: [121]
4 - first: romualdo second: perez ext: [141, 144]
英文:

This is just one way to do it (and probably an overly convoluted, not-so-great way that I haven't completely checked for everything). What I like about this is that it shows how to make a collector of your own, and they are fairly useful to understand those underpinnings. Hopefully the comments are explanation enough. If not, there are lots of guides on custom collectors (like this)

Person elpidio = Person.Builder().id(3L).firstName(&quot;elpidio&quot;).secondName(&quot;gomez&quot;).ext(&quot;121&quot;).build();
Person romual1 = Person.Builder().id(4L).firstName(&quot;romualdo&quot;).secondName(&quot;perez&quot;).ext(&quot;141&quot;).build();
Person romual2 = Person.Builder().id(4L).firstName(&quot;romualdo&quot;).secondName(&quot;perez&quot;).ext(&quot;144&quot;).build();
Map&lt;Long,Person&gt; filteredPersons = Stream.of( elpidio, romual1, romual2 ).collect(Collector.of(
HashMap::new,  // Starts with an empty list
(result,entry) -&gt; {
if ( result.containsKey(entry.id)) {
// If we already have this id in our map, we add the ext to the result list. This is assuming
// &quot;ext&quot; in Person is a collection of some sort (because I don&#39;t feel like going through the
// process of creating a new arbitrary class - in other words, that exercise is left for the
// reader =) If you don&#39;t want dupes of ext, make sure it&#39;s a set or something else that will
// remove dupes
result.get(entry.id).ext.addAll(entry.ext);
} else {
result.put(entry.id, entry);
}
},
(result1, result2) -&gt; {
// This handles the case where the collect is handled in parallel. How do we merge two resulting
// maps?
// In this case we are arbitrarily calling &quot;result1&quot; the version to check against, but it really
// doesn&#39;t matter.
for ( Map.Entry&lt;Long,Person&gt; person : result2.entrySet() ) {
if ( result1.containsKey(person.getKey()) ) {
result1.get(person.getKey()).ext.addAll(person.getValue().ext);
} else {
result1.put(person.getKey(), person.getValue());
}
}
return result1;
}));
filteredPersons.forEach( (k,v) -&gt; System.out.println( k + &quot; - first: &quot; + v.first + &quot; second: &quot; + v.second + &quot; &quot; +
&quot;ext: &quot; + v.ext));

outputs:

3 - first: elpidio second: gomez ext: [121]
4 - first: romualdo second: perez ext: [141, 144]

答案2

得分: 2

有许多方法可以解决这个问题。但是如果你想要按照你的参考链接中的答案来得到解答。

persons.stream()
    .collect(Collectors.groupingBy(Function.identity(), toList()))
    .entrySet()
    .stream()
    .map(x -> {
        List<String> exFields = x.getValue().stream().map(Person::getExField).flatMap(Collection::stream).collect(toList());
        Person duplicatePerson = x.getKey();
        duplicatePerson.setExField(exFields);
        return duplicatePerson;
    }).collect(toList());

解释:
在这里,我们尝试根据对象中的某些字段将对象分组。这可以通过重写 equalshashCode 方法来实现,如下所示:

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Person person = (Person) o;
    return Objects.equals(id, person.id) &&
            Objects.equals(firstName, person.firstName) &&
            Objects.equals(lastName, person.lastName);
}

@Override
public int hashCode() {
    return Objects.hash(id, firstName, lastName);
}

有了以上的代码,你将能够获得 entrySetentrySet 的键将是重复的对象(具有这三个字段中的任何对象),而值将是一个包含所有这些重复对象的列表。现在,从这个重复的列表中,你只需要每个对象的 exFields。所以这是代码:

List<String> exFields = x.getValue().stream().map(Person::getExField).flatMap(Collection::stream).collect(toList());

为什么我不得不使用 flatMap

一旦你获得了所有 exFields 的列表,你将把该列表分配给你的重复对象。

duplicatePerson.setExField(exFields);

最后进行收集操作。

英文:

There are a lot of ways to solve this question. However you if you want the answer as per your reference.

            persons.stream()
.collect(Collectors.groupingBy(Function.identity(), toList()))
.entrySet()
.stream()
.map(x -&gt; {
List&lt;String&gt; exFields = x.getValue().stream().map(Person::getExField).flatMap(Collection::stream).collect(toList());
Person duplicatePerson = x.getKey();
duplicatePerson.setExField(exFields);
return duplicatePerson;
}).collect(toList());

Explanation.
Here we are trying to group objects if certain fields are same in object. This can be done by overridinng the equals and HashCode method. like

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return Objects.equals(id, person.id) &amp;&amp;
Objects.equals(firstName, person.firstName) &amp;&amp;
Objects.equals(lastName, person.lastName);
}
@Override
public int hashCode() {
return Objects.hash(id, firstName, lastName);
}

With this much in place you will be able to get your entrySet. Keys of the entrySet as the duplicate object (any object with those three fields) and value will be a list containing all those duplicate objects. Now from this duplicate list you only need that exFields from each object so this is the code

List&lt;String&gt; exFields = x.getValue().stream().map(Person::getExField).flatMap(Collection::stream).collect(toList());

Why I had to use flatMap ?

Once you get the list of all the exFields you assign that list to your duplicate object.

 duplicatePerson.setExField(exFields);

Finally Collect.

答案3

得分: -1

你可以使用Java 8的Api Stream来通过谓词(predicate)按ID对它们进行筛选,或者使用distinct()方法。

https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html

英文:

You can use Java 8 Api Stream in order to filter them by id using a predicate or use distinct()

https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html

huangapple
  • 本文由 发表于 2020年8月28日 19:20:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/63632795.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定