patternMinor
Find unique variants of a product
Viewed 0 times
uniquevariantsproductfind
Problem
I am writing a piece of code that returns all the unique variants that a product is available in for an ecommerce app. For example, a shirt product can be available in different colors, sizes, and linen. If the available attributes are red, green, L, XL, Cotton, and Polyester, then a list of the unique variants should be eventually returned as:
{green; XL; Polyester}]
This would be the unique variants available for the product.
The code below works and eventually returns a string list of IDs representing each variant available for the product. The only problem that I am having with this is that it generates a duplicate of each variant. I can easily take care of that with a Set.ofList function after this code runs, but would like to solve that problem here internally. I'm new to F#, so what can I do to optimize this code?
```
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)
(current
[{red; L; Cotton} ; {red; L; Polyester} ; {red; XL; Cotton} ; {red; XL; Polyester} ; {green; L; Cotton} ; {green; XL; Cotton} ; {green; L; Polyester} ;{green; XL; Polyester}]
This would be the unique variants available for the product.
The code below works and eventually returns a string list of IDs representing each variant available for the product. The only problem that I am having with this is that it generates a duplicate of each variant. I can easily take care of that with a Set.ofList function after this code runs, but would like to solve that problem here internally. I'm new to F#, so what can I do to optimize this code?
```
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)
(current
Solution
If I understand correctly, you're looking for the Cartesian product of the attributes in each attribute category.
To get the Cartesian product, I've adapted* Eric Lippert's solution from his blog post Computing a Cartesian product with LINQ.
Then we need to group by the attribute category, and pull out just the attributes**.
Here is a test on the sample data you provided
Which gives:
* Hopefully without introducing errors.
**
This is a little bit nicer in C# since we have the overload of
To get the Cartesian product, I've adapted* Eric Lippert's solution from his blog post Computing a Cartesian product with LINQ.
let cartesianProduct xs =
Seq.fold (fun acc xs -> seq {
for accSeq in acc do
for x in xs do
yield Seq.append accSeq (Seq.singleton x)
}) (Seq.singleton Seq.empty) xsThen we need to group by the attribute category, and pull out just the attributes**.
let variants (attributes : seq) =
attributes
|> Seq.groupBy (fun attribute -> attribute.AttributeCategoryId)
|> Seq.map (snd >> Seq.map (fun attribute -> attribute.AttributeId))
|> cartesianProductHere is a test on the sample data you provided
let attributes = [
{ AttributeId = "red"; AttributeCategoryId = "Color" };
{ AttributeId = "L"; AttributeCategoryId = "Size" };
{ AttributeId = "XL"; AttributeCategoryId = "Size" };
{ AttributeId = "Cotton"; AttributeCategoryId = "Material" };
{ AttributeId = "green"; AttributeCategoryId = "Color" };
{ AttributeId = "Polyester"; AttributeCategoryId = "Material" }
]
for variant in variants attributes do
printfn "%A" variantWhich gives:
seq ["red"; "L"; "Cotton"]
seq ["red"; "L"; "Polyester"]
seq ["red"; "XL"; "Cotton"]
seq ["red"; "XL"; "Polyester"]
seq ["green"; "L"; "Cotton"]
seq ["green"; "L"; "Polyester"]
seq ["green"; "XL"; "Cotton"]
seq ["green"; "XL"; "Polyester"]* Hopefully without introducing errors.
**
This is a little bit nicer in C# since we have the overload of
GroupBy that takes an elementSelector parameter:return attributes.GroupBy(attribute => attribute.CategoryId, attribute => attribute.Id)
.CartesianProduct();Code Snippets
let cartesianProduct xs =
Seq.fold (fun acc xs -> seq {
for accSeq in acc do
for x in xs do
yield Seq.append accSeq (Seq.singleton x)
}) (Seq.singleton Seq.empty) xslet variants (attributes : seq<NewProductAttributeInfo>) =
attributes
|> Seq.groupBy (fun attribute -> attribute.AttributeCategoryId)
|> Seq.map (snd >> Seq.map (fun attribute -> attribute.AttributeId))
|> cartesianProductlet attributes = [
{ AttributeId = "red"; AttributeCategoryId = "Color" };
{ AttributeId = "L"; AttributeCategoryId = "Size" };
{ AttributeId = "XL"; AttributeCategoryId = "Size" };
{ AttributeId = "Cotton"; AttributeCategoryId = "Material" };
{ AttributeId = "green"; AttributeCategoryId = "Color" };
{ AttributeId = "Polyester"; AttributeCategoryId = "Material" }
]
for variant in variants attributes do
printfn "%A" variantseq ["red"; "L"; "Cotton"]
seq ["red"; "L"; "Polyester"]
seq ["red"; "XL"; "Cotton"]
seq ["red"; "XL"; "Polyester"]
seq ["green"; "L"; "Cotton"]
seq ["green"; "L"; "Polyester"]
seq ["green"; "XL"; "Cotton"]
seq ["green"; "XL"; "Polyester"]return attributes.GroupBy(attribute => attribute.CategoryId, attribute => attribute.Id)
.CartesianProduct();Context
StackExchange Code Review Q#110316, answer score: 6
Revisions (0)
No revisions yet.