user1206480
user1206480

Reputation: 1858

Return a list from recursive function

I'm trying to return a list from a function, but I'm getting an error that says that an unit was expected instead. Also, I would like to know if this code appears to be structured correctly in general.

code:

    let rec calculateVariants (attList: NewProductAttributeInfo list) (activeCount: int) 
    (currentList: (int * NewProductAttributeInfo) list) =

    // group attribute list by category id
    let attGrouped = attList |> List.groupBy (fun x -> x.AttributeCategoryId)

    // define mutable list
    let mutable stageList = currentList

    // begin iteration
    for catId,details in attGrouped do
        for d in details do
            if activeCount = 0
            then stageList <- (activeCount,d) :: stageList

            let groupLength = attGrouped.Length
            if (activeCount + 1) <= groupLength
            then
                let selectCat,selectDetails = attGrouped.[activeCount + 1]
                selectDetails
                    |> List.filter (fun x -> 
                        stageList
                        |> List.exists (fun (x') -> 
                            not(x' = (activeCount,x))))
                    |> (fun x ->
                        match x with
                        | [] -> ()
                        | head :: tail -> 
                            stageList <- (activeCount, head) :: stageList
                            let currentCategory = activeCount + 1
                            calculateVariants attList currentCategory stageList
                            )
    stageList // <-- error Unit expected

Upvotes: 0

Views: 155

Answers (2)

Petr
Petr

Reputation: 4280

if .. then .. else should return the same type on both branches. If you omit else branch then compiler assuming that it returns unit. Add else branch returning list.

Edit: Given your problem description, the easiest way would be something like this:

type NewProductAttributeInfo = {AttributeCategoryId: string; AttributeId: string}
let products = [ { AttributeCategoryId = "Size"; AttributeId = "S"};
                 { AttributeCategoryId = "Mat"; AttributeId = "Linen" };
                 { AttributeCategoryId = "Mat"; AttributeId = "Poliester" };
                 { AttributeCategoryId = "Color"; AttributeId = "White" };
                 { AttributeCategoryId = "Color"; AttributeId = "Green" };
                 { AttributeCategoryId = "Mat"; AttributeId = "Linen" };
                 { AttributeCategoryId = "Mat"; AttributeId = "Cotton" };
                 { AttributeCategoryId = "Mat"; AttributeId = "Poliester" };
                 { AttributeCategoryId = "Size"; AttributeId = "XL" } ]

let group list =
    list 
    |> Set.ofList // Provides uniqueness of attribute combinations
    |> Seq.groupBy (fun x -> x.AttributeCategoryId) // Grouping by CatId
    |> List.ofSeq

let res = group products

Result:

val it : (string * seq<NewProductAttributeInfo>) list =
 [("Color", seq [{AttributeCategoryId = "Color";
                  AttributeId = "Green";}; {AttributeCategoryId = "Color";
                                            AttributeId "White";}]);
  ("Mat",
   seq
     [{AttributeCategoryId = "Mat";
       AttributeId = "Cotton";}; {AttributeCategoryId = "Mat";
                                  AttributeId = "Linen";};
      {AttributeCategoryId = "Mat";
       AttributeId = "Poliester";}]);
  ("Size", seq [{AttributeCategoryId = "Size";
                 AttributeId = "S";}; {AttributeCategoryId = "Size";
                                       AttributeId = "XL";}])]

Upvotes: 2

user1206480
user1206480

Reputation: 1858

This is the solution that I came with. It works, but I'm sure it can be optimized quite a bit. I have a duplicate issue that is solved with the Set.ofList function externally after this code runs, which I'm still working on.

type NewProductAttributeInfo = {
    AttributeId : string;
    AttributeCategoryId : string
}


let rec private returnVariant (curIdx: int) (listLength: int) 
    (attList: (int * NewProductAttributeInfo * NewProductAttributeInfo) list) 
    (curList: NewProductAttributeInfo list) =

    match curList with
    | x when x.Length = listLength -> curList
    | x -> 
        let attTup =
            attList
            |> List.filter (fun x' -> 
                                let idx1,att1,att2' = x'
                                idx1 >= curIdx && not(curList 
                                                        |> List.exists (fun x'' -> 
                                                                            x'' = att2'))
                            )
        let idx1,att1,att2 = attTup |> List.head
        let newList = curList @ [att2]
        returnVariant idx1 newList.Length attList newList


let rec calculateVariants (attList: NewProductAttributeInfo list) 
    (currentList: (int * NewProductAttributeInfo * NewProductAttributeInfo) list) =

    // group attribute list by category id
    let attGrouped = attList |> List.groupBy (fun x -> x.AttributeCategoryId)
    let (firstGroupCatId,firstGroupDetails) = attGrouped.[0]




    match currentList with
    | [] ->
        let rawVariants = [for nxt in 0 .. (attGrouped.Length - 1) do
                            if nxt > 0
                            then
                                // begin iteration
                                for d in firstGroupDetails do
                                    let _,det = attGrouped.[nxt]
                                    for det' in det do
                                        yield (nxt, d, det')
                        ]
        calculateVariants attList rawVariants
    | x ->
        let groupLength = x |> List.groupBy (fun (idx,d0,nxtD) -> idx)
                        |> List.length |> ((+)1)
        let sortedGroup = x |> List.sortBy (fun (x,y,z) -> x)
        if groupLength > 2
        then // below is the block that generates the duplicates
            [for att in sortedGroup do
                for attCompare in sortedGroup do
                    let idx1,att1,att2 = att
                    let idx2,attC1,attC2 = attCompare
                    if idx2 > idx1 && att2 <> attC2
                    then
                        let idString = 
                            returnVariant idx2 groupLength x [att1; att2; attC2]
                            |> List.map (fun nl -> nl.AttributeId)
                        yield String.concat "," idString
            ]
        else
            [
                for att in sortedGroup do
                    let idx1,att1,att2 = att
                    let idString = 
                            returnVariant idx1 groupLength x [att1; att2]
                            |> List.map (fun nl -> nl.AttributeId)
                    yield String.concat "," idString
            ]

Upvotes: 0

Related Questions