ballerina-platform / ballerina-spec

Ballerina Language and Platform Specifications
Other
167 stars 53 forks source link

Flattening a list of lists into a list #369

Open sameerajayasoma opened 5 years ago

sameerajayasoma commented 5 years ago

This is not possible with the current set of operations in lang.array module. Consider the following example. I want to extract all the children into a single list. I can think of a sticky solution with nested loops, but a solution with function iteration would be ideal.

type Company record { Team[] teams; };
type Team record { Member[] members; };
type Member record {Child[] children; };
type Child record { string name; };

Team teamX = {members: [{children: [{name: "June"}, {name: "Peter"}, {name: "April"}, {name: "Jasmin"}]}, 
                {children: [{name: "Chris"}, {name: "Emily"}, {name: "Jack"}, {name: "Jill"}]}]};
Team teamY = {members: [{children: [{name: "Jannet"}, {name: "James"}, {name: "May"}, {name: "Tom"}]}]};
Company company = {teams: [teamX, teamY]};

public function main() {
    // With nested loops. 
    Child[] children = [];
    foreach var team in company.teams {
        foreach var member in team.members {
            foreach var child in member.children {
                children.push(child);
            }
        }    
    }

    Member[][] confs = company.teams.map(team => team.members);

    // With flatmap operation
    Child[] children = company
                              .teams
                              .flatmap(team => team.members)
                              .flatmap(member => member.children);

}

We can consider removing nil values (if any) with flatmap.

jclark commented 5 years ago

172 proposes adding concat, which makes this a bit easier (in conjunction with ... spread operator). I would do that before this.

I think the bar for adding functions to the lang library needs to be quite high. There is precedence for this function:

https://stackoverflow.com/questions/49843262/where-does-the-word-flatmap-originate-from

It’s also easy (and equally declarative) with foreach expressions proposed by query:

Child[] children = 
foreach var team in company.teams
    foreach var member in team.members
       foreach var child in member.children
           select child;
jclark commented 5 years ago

Just to be clear if L is a list of lists, you can flatten it into a list with array:concat(...L) (same as string:concat.) But it’s not terribly convenient for this usage in Ballerina because you cannot call it with method syntax; a function called flatten typically flattens to arbitrary depth, but we cannot write a type for that. What we need is a 1-arg concat, but I’m not sure what to call that.

I would want us to provide the flattening operation before the combination of flattening and mapping. Flattening also makes sense for XML.