1

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.

M. Justin
22.9k12 gold badges133 silver badges167 bronze badges
asked Dec 4, 2020 at 11:45
1
  • 1
    @fps Thanks for your question. No, that's completely dynamic Commented Dec 4, 2020 at 12:19

2 Answers 2

1

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());
answered Dec 4, 2020 at 12:28

3 Comments

Thanks for your detailed answer. The only thing I don't get from there you get the input.* in the map(e -> That's missing right?
Well, actually it is in this data initialization block I pasted (the second from the bottom), however, for your application you should rather have something like inputs[0].getAttribute1, inputs[0].getAttribute2 ... :) I'll edit the answer because it might be misleading.
Thanks for the edit. Now it's clear. I have now found another way via forEach. See below.
0

Thanks for your help Przemysław Gęsieniec. You inspired me to think in another direction and I think I found now a way:

KStream<String, AClass> output = inputs.flatMap(
 (key,value) -> {
 List<KeyValue<String, AClass>> result = new ArrayList<>();
 value.getAssets().forEach(asset -> {
 AClass transform = value.clone();
 transform.setAssets(Arrays.asList(asset));
 result.add(KeyValue.pair(key, transform));
 });
 return result;
 });
answered Dec 4, 2020 at 13:04

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.