当前位置: 代码迷 >> java >> 合并几乎相等的数据列表的值
  详细解决方案

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

热度:52   发布时间:2023-08-02 11:25:25.0

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

它的东西像一个配置器。 我得到的数据看起来像这样:

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.

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

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

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。 :它不是完整的代码,只是我正在尝试执行的示例。 我希望这次更容易理解:)

可能不是最优雅或最佳的解决方案,但是这里已经是一种快速的方法,可以根据您的描述给出结果。 它使用@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]}

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

  相关解决方案