Search code examples
javajava-stream

Split array in objects using the Java Stream API


I'm stuck in splitting an array in a sub array in an object. I have the following:

Input/Output AClass:

 {
    attribute1: 'value1',
    attribute2: 'value2',
    // etc.
    assets: [
      {assetAttribute1: 'value1', // etc.},
      {assetAttribute2: 'value2', // etc.}
      // etc.
    ]
  }

Stream Input:

inputs = [
     {
        attribute1: 'value1',
        attribute2: 'value2',
        assets: [
          {assetAttribute1: 'value1', // etc.},
          {assetAttribute2: 'value2', // etc.}
          // etc.
        ]
      },
    
    ]

Expected stream output:

outputs = [
     {
        attribute1: 'value1', // same as from the input
        attribute2: 'value2', // same as from the input
        assets: [
          {assetAttribute1: 'value1', // etc.} // input asset array index 0
        ]
      },
      {
        attribute1: 'value1', // same as from the input
        attribute2: 'value2', // same as from the input
        assets: [
          {assetAttribute2: 'value2', // etc.} // // input asset array index 1
        ]
      },
    ]

Kafka Streams code:

KStream<String, AClass> inputs = //...
KStream<String, AClass> output = inputs.map((key, aclass) -> {
                return aclass.getAssets().stream().map(asset -> {
                    AClass transform = new AClass();
                    transform = aclass.clone();
                    transform.setAssets(Arrays.asList(asset));
                    return new KeyValue<String, AClass>(key, miaTransitAsset);
                });
            });

As you see this can not work. But I'm now stuck how I could achieve that with the Java Streams. You can ignore the Kafka stream as it basically the same.


Solution

  • It is manageable to do it with one stream however, for readability let mi split this fist:

    For simplicity, I created myself a class (hopefully got it right from the JSON description):

    class Input {
        String attribute1;
        String attribute2;
        List<Map<String, String>> assets;
    
        public Input(String attribute1, String attribute2, List<Map<String, String>> assets) {
            this.attribute1 = attribute1;
            this.attribute2 = attribute2;
            this.assets = assets;
        }
    }
    

    Then some dumb initialization to have some data to work on:

        List<Map<String, String>> assets = new ArrayList<>();
        Map<String, String> attributeMap1 = new HashMap<>();
        Map<String, String> attributeMap2 = new HashMap<>();
    
        attributeMap1.put("assetAttribute1", "value1");
        attributeMap2.put("assetAttribute2", "value2");
    
        assets.add(attributeMap1);
        assets.add(attributeMap2);
        Input input = new Input("value1", "value2", assets);
    
        List<Input> inputs = Arrays.asList(input);
    

    And here come the lambdas:

    First, you have to get all the attributes you want to extract (list of assets over which you will create new objects):

    List<Map<String, String>> extractedAssets = inputs
                .stream()
                .map(e -> e.assets)
                .flatMap(Collection::stream)
                .collect(Collectors.toList());
    

    Then for each of these assets, you need a copy of what you had in input previously, but with the difference of "assets" list content. So the best way is to create a new object for each previously extractedAsset:

    List<Input> collect = extractedAssets.stream()
                .map(e -> new Input(
                        input.attribute1,
                        input.attribute2,
                        Collections.singletonList(e)))
                .collect(Collectors.toList());
    

    This should work :)

    Or with one stream:

    List<Input> collect1 = inputs.
                stream()
                .map(e -> e.assets)
                .flatMap(Collection::stream)
                .map(e -> new Input(
                        inputs.get(0).attribute1,
                        inputs.get(0).attribute2,
                        Collections.singletonList(e)))
                .collect(Collectors.toList());