如何创建类似于三个不同类型列表的笛卡尔积的数据结构?

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

How to create a data structure similar to the cartesian product of three lists of different types?

问题

I want to create a DataStructure which will be similar to the cartesian product of three List.
I've also referred the existing answer by Jurgen which suggests to use flatMap. I tried in that way as well. But I've a condition that filterValue list is inside types list. So flatMap will not work here. As filterValues can be 0 or more. So depending on that cartesian product (we might call it as combination) will change.

Size of measures, types & filterValues can be different for each list.
If measure list is empty. Then the combination will be of only types & filterValues (and measure will be set to null. I've added these different scenarios in my comments of if-else block

I've following types of list:

  1. List<String> measures
  2. List<Type> types
  3. List<FilterValue> filterValues

For example the Input structure is:

{
  "measures": [
    "m1",
    "m2",
    "m3"
  ],
  "types": [
    {
      "type": "type-1",
      //some more fields
      "filterValues": [
        //no filter values present
      ]
    },
    {
      "type": "type-2",
      //some more fields
      "filterValues": [
        {
          "filterValue": "t2f1"
          //some more fields
        },
        {
          "filterValue": "t2f2"
          //some more fields
        }
      ]
    }
  ]
}

Then in the above case the output data structure I'm expecting is

m1	type-1 null
m1 	type-2 t2f1 
m1 	type-2 t2f2	

m2	type-1 null
m2 	type-2 t2f1 
m2 	type-2 t2f2	

m3	type-1 null
m3 	type-2 t2f1 
m3 	type-2 t2f2	

Then the same above values I'm setting into the following classes:

class SearchArea {
    String measure;
    String type;
    TypeCombi typeFilter;
    //constructor for measure & type
    //constructor for all three
    //getters & setters
}

class TypeCombi {
    String type;
    String name; //it is mapped with filterValue
    //constructor for above two fields
    //getters & setters
}

The class Type & FilterValue is as below

class Type {
    String type;
    List<FilterValue> filterValues;
    //some more fields
    //getters and setters
}

class FilterValue {
    String filterValue;
    //some more fields
    //getters and setters
}

I'm able to achieve the expected output using the following getSearchAreas function. But in this case I'm using multiple(two) for loops. Can this code block be cleaned up using stream/flatMap instead of two for loops?
Also is there any better way to handle multiple if/else block? (I've added a comment above each if/else block for its scenario)

private List<SearchArea> getSearchAreas(List<String> measures, List<Type> types){
    List<SearchArea> searchAreas = new ArrayList<>();

    //measures & types both are empty
    if ((measures == null || measures.isEmpty())
            && (types == null || types.isEmpty()))
        return Collections.emptyList();

    //one or more measure and zero types
    else if (measures != null && !measures.isEmpty()
            && (types == null || types.isEmpty())) {
        searchAreas = measures
                .stream()
                .map(measure -> new SearchArea(measure, null))
                .collect(Collectors.toList());
        return searchAreas;
    }
    //zero measures and one or more types
    else if ((measures == null || measures.isEmpty())) {
        for (Type type : types) {
            if (type.getFilterValues() == null
                    || type.getFilterValues().isEmpty()) {
                searchAreas.add(new SearchArea(null, type.getType()));
            } else {
                searchAreas.addAll(type.getFilterValues()
                        .stream()
                        .map(filterValue -> new SearchArea(null,
                                type.getType(),
                                new TypeCombi(type.getType(),
                                        filterValue.getFilterValue())))
                        .collect(Collectors.toList()));
            }
        }
        return searchAreas;
    }
    //one or more measures and one or more types
    else {
        for (String measure : measures) {
            for (Type type : types) {
                if (type.getFilterValues() == null
                        || type.getFilterValues().isEmpty()) {
                    searchAreas.add(new SearchArea(measure, type.getType()));
                } else {
                    searchAreas.addAll(type.getFilterValues()
                            .stream()
                            .map(filterValue -> new SearchArea(measure,
                                    type.getType(),
                                    new TypeCombi(type.getType(),
                                            filterValue.getFilterValue())))
                            .collect(Collectors.toList()));
                }
            }
        }
        return searchAreas;
    }
}

It will be great if someone can help me restructure the above in a cleaner fashion.

英文:

I want to create a DataStructure which will be similar to the cartesian product of three List.
I've also referred the existing answer by Jurgen which suggest to use flatMap. I tried in that way as well. But I've condition that filterValue list is inside types list. So flatMap will not work here. As filterValues can be 0 or more. So depending on that cartesian product (we might call it as combination) will change.

Size of measures, types &amp; filterValues can be different for each list.
If measure list is empty. Then the combination will be of only types &amp; filterValues (and measure will be set to null. I've added these different scenarios in my comments of if-else block

I've following types of list:

  1. List&lt;String&gt; measures
  2. List&lt;Type&gt; types
  3. List&lt;FilterValue&gt; filterValues

For example the Input structure is:

{
&quot;measures&quot;: [
&quot;m1&quot;,
&quot;m2&quot;,
&quot;m3&quot;
],
&quot;types&quot;: [
{
&quot;type&quot;: &quot;type-1&quot;,
//some more fields
&quot;filterValues&quot;: [
//no filter values present
]
},
{
&quot;type&quot;: &quot;type-2&quot;,
//some more fields
&quot;filterValues&quot;: [
{
&quot;filterValue&quot;: &quot;t2f1&quot;
//some more fields
},
{
&quot;filterValue&quot;: &quot;t2f2&quot;
//some more fields
}
]
}
]
}

Then in above case the output data structure I'm expecting is

m1	type-1 null
m1 	type-2 t2f1 
m1 	type-2 t2f2	

m2	type-1 null
m2 	type-2 t2f1 
m2 	type-2 t2f2	

m3	type-1 null
m3 	type-2 t2f1 
m3 	type-2 t2f2	

Then the same above values I'm setting into the following classes:

class SearchArea {
    String measure;
    String type;
    TypeCombi typeFileter;
    //constructor for measure &amp; type
    //constructor for all three
    //getters &amp; setters
}

class TypeCombi {
    String type;
    String name; //it is mapped with filterValue
    //constructor for above two fields
    //getters &amp; setters
}

The class Type & FilterValue is as below

class Type {
    String type;
    List&lt;FilterValue&gt; filterValues;
    //some more fields
    //getters and setters
}

class FilterValue {
    String filterValue;
    //some more fields
    //getters and setters
}

I'm able to achieve the expected output using following getSearchAreas function. But in this case I'm using multiple(two) for loops. Can this code block cleaned up using stream/flatmap instead of two for loops ?
Also is there any better way to handle multiple if/else block ?(I've added comment above each if/else block for it's scenario)

private List&lt;SearchArea&gt; getSearchAreas(List&lt;String&gt; measures, List&lt;Type&gt; types){
    List&lt;SearchArea&gt; searchAreas = new ArrayList&lt;&gt;();

    //measures &amp; types both are empty
    if ((measures == null || measures.isEmpty())
            &amp;&amp; (types == null || types.isEmpty()))
        return Collections.emptyList();

    //one or more measure and zero types
    else if (measures != null &amp;&amp; !measures.isEmpty()
            &amp;&amp; (types == null || types.isEmpty())) {
        searchAreas = measures
                .stream()
                .map(measure -&gt; new SearchArea(measure, null))
                .collect(Collectors.toList());
        return searchAreas;
    }
    //zero measures and one or more types
    else if ((measures == null || measures.isEmpty())) {
        for (type type : types) {
            if (type.getFilterValues() == null
                    || type.getFilterValues().isEmpty()) {
                searchAreas.add(new SearchArea(null, type.getType()));
            } else {
                searchAreas.addAll(type.getFilterValues()
                        .stream()
                        .map(filterValue -&gt; new SearchArea(null,
                                type.getType(),
                                new TypeCombi(type.getType(),
                                        filterValue.getFilterValue())))
                        .collect(Collectors.toList()));
            }
        }
        return searchAreas;
    }
    //one or more measures and one or more types
    else {
        for (String measure : measures) {
            for (Type type : types) {
                if (type.getFilterValues() == null
                        || type.getFilterValues().isEmpty()) {
                    searchAreas.add(new SearchArea(measure, type.getType()));
                } else {
                    searchAreas.addAll(type.getFilterValues()
                            .stream()
                            .map(filterValue -&gt; new SearchArea(measure,
                                    type.getType(),
                                    new TypeCombi(type.getType(),
                                            filterValue.getFilterValue())))
                            .collect(Collectors.toList()));
                }
            }
        }
        return searchAreas;
    }
}

It will be great if someone can help me in restructuring above in cleaner fashion.

答案1

得分: 1

这是你想要的内容。请注意,有时不使用流可能更清晰。

public static void main(String[] args) throws Exception {
    List<String> strings = Collections.emptyList();
    List<Integer> ints = Arrays.asList(1, 2, 3);

    if (strings == null || strings.isEmpty()) {
        strings = Collections.singletonList(null);
    }

    if (ints == null || ints.isEmpty()) {
        ints = Collections.singletonList(null);
    }

    for (String str : strings) {
        for (Integer integer : ints) {
            // In your code doubles comes from a property of integer.
            List<Double> doubles = integer == null ? Collections.emptyList() : Arrays.asList(1.0d, 2.0d, 3.0d);

            if (doubles == null || doubles.isEmpty()) {
                doubles = Collections.singletonList(null);
            }

            for (Double doubler : doubles) {
                // Create your object here.
                System.out.format(Locale.US, "    str = %s, int = %d, double = %f %n", str, integer, doubler);
            }
        }
    }
}

Output follows:

    str = null, int = 1, double = 1.000000 
    str = null, int = 1, double = 2.000000
    str = null, int = 1, double = 3.000000
    str = null, int = 2, double = 1.000000
    str = null, int = 2, double = 2.000000 
    str = null, int = 2, double = 3.000000
    str = null, int = 3, double = 1.000000
    str = null, int = 3, double = 2.000000
    str = null, int = 3, double = 3.000000
英文:

I think this is what you want. Note that it is sometimes cleaner not to use streams.

public static void main(String[] args) throws Exception {
List&lt;String&gt; strings = Collections.emptyList();
List&lt;Integer&gt; ints = Arrays.asList(1, 2, 3);
if (strings == null || strings.isEmpty()) {
strings = Collections.singletonList(null);
}
if (ints == null || ints.isEmpty()) {
ints = Collections.singletonList(null);
}
for (String str : strings) {
for (Integer integer : ints) {
// In your code doubles comes from a property of integer.
List&lt;Double&gt; doubles = integer == null ? Collections.emptyList() : Arrays.asList(1.0d, 2.0d, 3.0d);
if (doubles == null || doubles.isEmpty()) {
doubles = Collections.singletonList(null);
}
for (Double doubler : doubles) {
// Create your object here.
System.out.format(Locale.US, &quot;    str = %s, int = %d, double = %f %n&quot;, str, integer, doubler);
}
}
}
}

Output follows:

str = null, int = 1, double = 1.000000 
str = null, int = 1, double = 2.000000
str = null, int = 1, double = 3.000000
str = null, int = 2, double = 1.000000
str = null, int = 2, double = 2.000000 
str = null, int = 2, double = 3.000000
str = null, int = 3, double = 1.000000
str = null, int = 3, double = 2.000000
str = null, int = 3, double = 3.000000

答案2

得分: 1

以下是您提供的内容的翻译:

您可以获得三个或更多不同类型的列表的笛卡尔积,并将其存储在一个对象的列表列表 List&lt;List&lt;Object&gt;&gt; 中。

public static List&lt;List&lt;Object&gt;&gt; cartesianProduct(List&lt;?&gt;... lists) {
    // 不正确的输入数据
    if (lists == null) return Collections.emptyList();
    return Arrays.stream(lists)
            // 非空且非空列表
            .filter(list -&gt; list != null &amp;&amp; list.size() &gt; 0)
            // 将每个列表元素表示为 SingletonList&lt;Object&gt;
            .map(list -&gt; list.stream().map(Collections::&lt;Object&gt;singletonList)
                    // Stream&lt;List&lt;List&lt;Object&gt;&gt;&gt;
                    .collect(Collectors.toList()))
            // 内部列表对的求和
            .reduce((list1, list2) -&gt; list1.stream()
                    // 内部列表的组合
                    .flatMap(inner1 -&gt; list2.stream()
                            // 将两个内部列表合并为一个
                            .map(inner2 -&gt; Stream.of(inner1, inner2)
                                    .flatMap(List::stream)
                                    .collect(Collectors.toList())))
                    // 组合列表
                    .collect(Collectors.toList()))
            // 返回 List&lt;List&lt;Object&gt;&gt;,否则返回空列表
            .orElse(Collections.emptyList());
}
public static void main(String[] args) {
    List&lt;Integer&gt; list1 = Arrays.asList(1, 2);
    List&lt;String&gt; list2 = Arrays.asList("A", "B");
    List&lt;Object&gt; list3 = Arrays.asList(null, "NULL");
    List&lt;Time&gt; list4 = Collections.singletonList(new Time(0));

    List&lt;List&lt;Object&gt;&gt; lists = cartesianProduct(list1, list2, list3, list4);
    // 输出
    lists.forEach(System.out::println);
}

输出:

[1, A, null, 03:00:00]
[1, A, NULL, 03:00:00]
[1, B, null, 03:00:00]
[1, B, NULL, 03:00:00]
[2, A, null, 03:00:00]
[2, A, NULL, 03:00:00]
[2, B, null, 03:00:00]
[2, B, NULL, 03:00:00]

<sup>另请参见:查找两个列表的笛卡尔积</sup>

英文:

You can get the Cartesian product of three or more lists of different types and store it into a list of lists of objects List&lt;List&lt;Object&gt;&gt;.

public static List&lt;List&lt;Object&gt;&gt; cartesianProduct(List&lt;?&gt;... lists) {
    // incorrect incoming data
    if (lists == null) return Collections.emptyList();
    return Arrays.stream(lists)
            // non-null and non-empty lists
            .filter(list -&gt; list != null &amp;&amp; list.size() &gt; 0)
            // represent each list element as SingletonList&lt;Object&gt;
            .map(list -&gt; list.stream().map(Collections::&lt;Object&gt;singletonList)
                    // Stream&lt;List&lt;List&lt;Object&gt;&gt;&gt;
                    .collect(Collectors.toList()))
            // summation of pairs of inner lists
            .reduce((list1, list2) -&gt; list1.stream()
                    // combinations of inner lists
                    .flatMap(inner1 -&gt; list2.stream()
                            // merge two inner lists into one
                            .map(inner2 -&gt; Stream.of(inner1, inner2)
                                    .flatMap(List::stream)
                                    .collect(Collectors.toList())))
                    // list of combinations
                    .collect(Collectors.toList()))
            // returns List&lt;List&lt;Object&gt;&gt;, otherwise an empty list
            .orElse(Collections.emptyList());
}
public static void main(String[] args) {
    List&lt;Integer&gt; list1 = Arrays.asList(1, 2);
    List&lt;String&gt; list2 = Arrays.asList(&quot;A&quot;, &quot;B&quot;);
    List&lt;Object&gt; list3 = Arrays.asList(null, &quot;NULL&quot;);
    List&lt;Time&gt; list4 = Collections.singletonList(new Time(0));

    List&lt;List&lt;Object&gt;&gt; lists = cartesianProduct(list1, list2, list3, list4);
    // output
    lists.forEach(System.out::println);
}

Output:

[1, A, null, 03:00:00]
[1, A, NULL, 03:00:00]
[1, B, null, 03:00:00]
[1, B, NULL, 03:00:00]
[2, A, null, 03:00:00]
[2, A, NULL, 03:00:00]
[2, B, null, 03:00:00]
[2, B, NULL, 03:00:00]

<sup>See also: Find cartesian product of 2 lists</sup>

答案3

得分: 0

你可以创建一个通用方法,接受不同类型的列表 List<? extends R>,并返回它们的超类型列表 List<R>

Try it online!

/**
 * @param lists 用于乘法的列表的列表
 * @param <R>   元素的超类型
 * @return 笛卡尔积
 */
public static <R> List<List<R>> cartesianProduct(List<List<? extends R>> lists) {
    // 检查传入的数据是否不为空
    if (lists == null) return Collections.emptyList();
    // 笛卡尔积,中间结果
    List<List<R>> cp = Collections.singletonList(Collections.emptyList());
    // 遍历传入的列表
    for (List<? extends R> list : lists) {
        // 非空且非空列表
        if (list == null || list.size() == 0) continue;
        // 下一次迭代的中间结果
        List<List<R>> next = new ArrayList<>();
        // 当前中间结果的行
        for (List<R> row : cp) {
            // 当前列表的元素
            for (R el : list) {
                // 下一个中间结果的新行
                List<R> nRow = new ArrayList<>(row);
                nRow.add(el);
                next.add(nRow);
            }
        }
        // 传递到下一次迭代
        cp = next;
    }
    // 笛卡尔积,最终结果
    return cp;
}
public static void main(String[] args) {
    List<Integer> l1 = Arrays.asList(1, 2);
    List<Long> l2 = Arrays.asList(3L, 4L);
    List<Double> l3 = Arrays.asList(5.5D, 6.6D);

    List<List<Number>> cp = cartesianProduct(Arrays.asList(l1, l2, l3));
    // 输出
    for (List<Number> row : cp) System.out.println(row);
}

输出:

[1, 3, 5.5]
[1, 3, 6.6]
[1, 4, 5.5]
[1, 4, 6.6]
[2, 3, 5.5]
[2, 3, 6.6]
[2, 4, 5.5]
[2, 4, 6.6]

另请参阅:如何从多个列表中获取笛卡尔积?

英文:

You can create a generic method that accepts lists of different types List&lt;? extends R&gt; and returns a lists of their supertype List&lt;R&gt;.

Try it online!

/**
 * @param lists a list of lists for multiplication
 * @param &lt;R&gt;   the supertype of the elements
 * @return the Cartesian product
 */
public static &lt;R&gt; List&lt;List&lt;R&gt;&gt; cartesianProduct(List&lt;List&lt;? extends R&gt;&gt; lists) {
    // check if incoming data is not null
    if (lists == null) return Collections.emptyList();
    // Cartesian product, intermediate result
    List&lt;List&lt;R&gt;&gt; cp = Collections.singletonList(Collections.emptyList());
    // iterate through incoming lists
    for (List&lt;? extends R&gt; list : lists) {
        // non-null and non-empty lists
        if (list == null || list.size() == 0) continue;
        // intermediate result for next iteration
        List&lt;List&lt;R&gt;&gt; next = new ArrayList&lt;&gt;();
        // rows of current intermediate result
        for (List&lt;R&gt; row : cp) {
            // elements of current list
            for (R el : list) {
                // new row for next intermediate result
                List&lt;R&gt; nRow = new ArrayList&lt;&gt;(row);
                nRow.add(el);
                next.add(nRow);
            }
        }
        // pass to next iteration
        cp = next;
    }
    // Cartesian product, final result
    return cp;
}
public static void main(String[] args) {
    List&lt;Integer&gt; l1 = Arrays.asList(1, 2);
    List&lt;Long&gt; l2 = Arrays.asList(3L, 4L);
    List&lt;Double&gt; l3 = Arrays.asList(5.5D, 6.6D);

    List&lt;List&lt;Number&gt;&gt; cp = cartesianProduct(Arrays.asList(l1, l2, l3));
    // output
    for (List&lt;Number&gt; row : cp) System.out.println(row);
}

Output:

[1, 3, 5.5]
[1, 3, 6.6]
[1, 4, 5.5]
[1, 4, 6.6]
[2, 3, 5.5]
[2, 3, 6.6]
[2, 4, 5.5]
[2, 4, 6.6]

<sup>See also: How to get Cartesian product from multiple lists?</sup>

1: https://tio.run/##hVTBThsxEL3vV8xxE4WlQOHQhLQVXCqhCoVj1YPxOonBa1v2GEpLvj0dO5tks7vASoll@828meeZeWBP7MhYoR/Kx/VaVtY4hAc6LAJKVQzHWefsRnocZxlXzHu4uoV/GdB3PBymFYbwzTLHKlCE88DSCmZe7@fGQRUUSqskZyiNbplNZlPa4lKAD1Y4fLEiWscDoUQlNPqdhRMYnE53V8yh8JJpsM6UgWMNOk6rDfdEBx6JkSeKmMUk/c2mU@Bb69uNcb6//griDwpdeojAlMSgzjnlfQx8KfgjyDlIzU0l9QJKhgykB20QdFBqhyZQvtHh8jLdDKDO4cooJXjUwxeisvgSyfPBuEnUyXFElChcJUrJUJArT8ruLFopWrg8YPEUqRJodGJ6g/8wAElkkQiXzoTFcp9wymmHjG@cd8TbFMKXroS1c230UZQEmC7TJoXRct0UcashvL4mFGX0V@SDePxpANxolDqIcZunR7IUsaZQ6wx3VdmrZAIStXiG786xpNRk2nyrmsmZZx9rlwfnqG7ffaxD3UgtMiaxuG0rVfve9kLTfxShg01OZ4Svpe/zt30ByijS7tX4IOADfShmPSPrrjLksyXO9osGBSvLXKi3EBRHQkRoD2aVvb2jlGwcUmjee9zUGPF@73z1QdfNpWaqLUjdyNxu/KyynsnzZGQJFZM6v0NHjfPrNzC3OOiGpNkPEn4hHPXMCQWXtPQF86knT0Zw2hBiU5tGLwh82gGf3Yzg800bfm0oKkEGZx2D8@L8egQXxcU1GfWMkp@huqfA6nnSmZuHzhTFqk7pd9YaJCagDdgzMGr3jeq/e/EoqoIsCkuSodKNelplq/X6Pw "Java (OpenJDK 8) – Try It Online"
2: https://stackoverflow.com/a/68629936/16596785

huangapple
  • 本文由 发表于 2020年8月13日 23:18:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/63398192.html
匿名

发表评论

匿名网友

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

确定