合并几乎相等的数据列表的值

时间:2019-02-22 06:16:24

标签: java list merge java-stream

所以我之前问过,但似乎我对我所说的内容还不够清楚,所以我现在想让它更清楚: 我试图做的是为导入准备数据。我获得的数据是人为的,因此效率不是很高,因此,我删除了不必要的条目并尝试尽可能地合并数据。

用于配置程序。我得到的数据看起来像这样:

123:45:AB = 12 这意味着:如果选项1为1或2或3,选项2为4或5,选项3为A或B,则结果将为1 AND 2

我创建了一个类似这样的类:

Class Options{
    String opt1;
    String opt2;
    String opt3;
    String optResult;

    //and some other stuff

    boolean hasSameOptions(Options o){
        return opt1.equals(o.opt1) && opt2.equals(o.opt2) && opt3.equals(o.opt3);
    }

    public void AddOptions(String options) {
        for (String s : options.split("")) {
            if (!optResult.contains(s)) {
                optResult = optResult + s;
            }
        }
    }

}

现在,数据是重复的并且可以合并。喜欢:

12 : 45 : AB = 12
12 : 45 : AB = 3
12 : 45 : AB = 4

这实际上意味着:12:45:AB = 1234

所以,我要做的是将字符串分开以仅获得单个值的结果,例如:

1 : 4 : A = 12
1 : 4 : B = 12
1 : 5 : A = 12 
//and so on.

我列出所有这些值,然后尝试再次组合它们以获得更有效的列表。

我要做的第一步是获取所有具有相同选项但结果不同的对象,然后合并结果。这样的事情发生:

public static List<Options> cleanList(List<Options> oldList) {

    List<Options> newList = new ArrayList<>();
    for (Options item : oldList) {
        Options temp = findEqualOptions(newList, item);
        if (temp != null)
            temp.AddOptions(item.optResult);
        else
            newList.add(item);
    }

    return newList;
}

public static <T> T findByProperty(Collection<T> col, Predicate<T> filter) {
    return col.stream().filter(Objects::nonNull).filter(filter).findFirst().orElse(null);
}

public static Options findEqualOptions(List<Options> list, Options opt) {
    return findByProperty(list, d -> d.hasSameOptions(opt));
}

在那之后,我尝试通过组合只有一个不同值的元素来进一步压缩列表。例如:

1 : 2 : A = 12
1 : 3 : A = 12 
 -> 1 : 23 : A = 12

我这样做:

for (int i = 0; i < list.size(); i++) {
    for (int j = i + 1; j < list.size(); j++) {
        Option o1 = list.get(i);
        Option o2 = list.get(j);
        int diff1 = 0;
        int diff2 = 0;
        int diff3 = 0;
        int diff4 = 0;


        if(!o1.opt1.equals(o2.opt1))
            diff1 = 1;
        if(!o1.opt2.equals(o2.opt2))
            diff2 = 1;

        //and so on

        if((diff1+diff2+diff3+diff4)>1)
            continue;

        if(diff1 == 1)
            o1.opt1 = o1.opt1 + o2.opt1;

        //and so on...


        list.remove(j--);


    }
}

我这样做直到没有更多更改为止。它运作良好,但速度缓慢。特别是方法cleanList()。 有谁知道如何使它变得更好吗?我试图使用流直接像这样获得整个equals选项列表:

public static <T> List<T> findByMultipleValue(Collection<T> col, Predicate<T> filter) {
    return col.stream().filter(filter).collect(Collectors.toList());
}

public static List<Options> getEqualOptionsList(List<Options> optList, Options opt){
    return findByMultipleValue(optList, o -> o.hasSameOptions(opt));
}

但这使它变慢了很多。

PS。 :它不是完整的代码,只是我正在尝试执行的示例。我希望这次更容易理解:)

1 个答案:

答案 0 :(得分:0)

可能不是最优雅或最佳的解决方案,但这里已经是一种快速的方法,可以根据您的描述给出结果。它使用@Joseph Larson的评论中建议的HashMap

我去找了一组字符,以确保其中的值不重复,但可以随时进行调整:)

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

class Scratch {
    public static class Option{
        String opt1;
        String opt2;
        String opt3;
        String optResult;

        public Option(String opt1, String opt2, String opt3, String optResult) {
            this.opt1 = opt1;
            this.opt2 = opt2;
            this.opt3 = opt3;
            this.optResult = optResult;
        }

        public static String merge(String a, String b){
            StringBuilder value = new StringBuilder();
            Set<Character> result = new HashSet<>();
            for(char c : a.toCharArray()){
                result.add(c);
            }
            for(char c : b.toCharArray()){
                result.add(c);
            }
            for(char c : result){
                value.append(c);
            }
            return value.toString();
        }

        public Option(Option a, Option b) {
            this(merge(a.opt1, b.opt1), merge(a.opt2, b.opt2), merge(a.opt3, b.opt3), merge(a.optResult, b.optResult));
        }

        String getKey(){
            return String.join(":", opt1, opt2, opt3);
        }

        int distance(Option option){
            int diff1 = this.opt1.equals(option.opt1)?0:1;
            int diff2 = this.opt2.equals(option.opt2)?0:1;
            int diff3 = this.opt3.equals(option.opt3)?0:1;
            int diff4 = this.optResult.equals(option.optResult)?0:1;
            return diff1 + diff2 + diff3 + diff4;
        }

        public String toString(){
            return getKey();
        }
    }
    public static void main(String[] args) {
        Option[] data = new Option[]{
                new Option("12", "45", "AB", "12"),
                new Option("12", "45", "AB", "3"),
                new Option("12", "45", "AB", "4"),
                new Option("12", "45", "AC", "1"),
                new Option("12", "45", "AC", "12"),
                new Option("3", "45", "AC", "13"),
                new Option("12", "45", "AD", "12"),
        };

        mergeExact(data);
        mergeClose(data, 1);
    }

    private static void mergeClose(Scratch.Option[] data, int distance){
        Map<Option, Set<Character>> buffer = new HashMap<>();
        for(Option option : data) {
            boolean found = false;
            Option toDelete = null;
            for(Map.Entry<Option, Set<Character>> entry : buffer.entrySet()){
                if(option.distance(entry.getKey()) <= distance){
                    Option merged = new Option(entry.getKey(), option);
                    for(char c : option.optResult.toCharArray()){
                        entry.getValue().add(c);
                    }
                    buffer.put(merged, entry.getValue());
                    toDelete = entry.getKey();
                    found = true;
                    break;
                }
            }
            if(found) {
                buffer.remove(toDelete);
            }else{
                Set<Character> set = new HashSet<>();
                for(char c : option.optResult.toCharArray()){
                    set.add(c);
                }
                buffer.put(option, set);
            }
        }
        System.out.println(String.format("merge with distance of %d:: %s", distance, buffer));

    }

    private static void mergeExact(Scratch.Option[] data) {
        Map<String, Set<Character>> buffer = new HashMap<>();
        for(Option option : data){
            Set<Character> item = buffer.computeIfAbsent(option.getKey(), k -> new HashSet<>());
            for(char c : option.optResult.toCharArray()){
                item.add(c);
            }
        }
        System.out.println("exact merge:: "+buffer);
    }


}

输出是

exact merge:: {3:45:AC=[1, 3], 12:45:AD=[1, 2], 12:45:AC=[1, 2], 12:45:AB=[1, 2, 3, 4]}
merge with distance of 1:: {12:45:AB=[1, 2, 3, 4], 3:45:AC=[1, 3], 12:45:ACD=[1, 2]}

编辑:遗漏了一部分问题,当差异接近时更新以添加合并。就优化而言,这部分甚至可能比第一部分更糟糕,但这是一个可行的基础:)