Defining Entity Framework Keys using Fluent API - entity-framework

I am trying to define a key for a model type that has two key properties and is defined like this:
type Model () =
member val IdOne = 0 with get, set
member val IdTwo = 0 with get, set
member val OtherProperty = "" with get, set
When I try to use this model in Entity Framework 5, I get the error that "Model has no key defined. Define the key for this EntityType". The model types are given, I cannot change them and add the [<Key>] attribute. So I tried the Fluent API.
In C#, you would do something like this:
modelBuilder.Entity<Model>().HasKey(m => new { m.IdOne, m.IdTwo });
It uses an anonymous type. But for the life of me I cannot figure out how to pull this off in F#. I tried Tuples, Records, even a regular type that has the properties IdOne and IdTwo:
// Regular type with properties IdOne & IdTwo.
type ModelKey (idOne, idTwo) =
member this.IdOne = idOne
member this.IdTwo = idTwo
modelBuilder.Entity<Model>().HasKey(fun (m : Model) -> ModelKey (m.IdOne, m.IdTwo))
// ArgumentNullException: Value cannot be null. Parameter name: source
// Regular type with default constructor and properties IdOne & IdTwo.
type ModelKey2 () =
member val IdOne = 0 with get, set
member val IdTwo = 0 with get, set
modelBuilder.Entity<Model>().HasKey(fun (m : Model) -> ModelKey2 ())
// ArgumentNullException: Value cannot be null. Parameter name: source
// Record type.
type ModelKeyRecord = { IdOne : Int32; IdTwo : Int32 }
modelBuilder.Entity<Model>().HasKey(fun (m : Model) -> { IdOne = m.IdOne; IdTwo = m.IdTwo })
// ArgumentNullException: Value cannot be null. Parameter name: source
// Tuple.
modelBuilder.Entity<Model>().HasKey(fun (m : Model) -> (m.IdOne, m.IdTwo))
// ArgumentNullException: Value cannot be null. Parameter name: source
None of these approaches work, I get an ArgumentNullException every time. I'm out of ideas...
EDIT
With the code Tomas provided below (that causes the same ArgumentNullException btw.), I did some snooping around. This is what I found:
I used the function below to analyze the Expression Tree C# is building:
static void Analyze<T>(Expression<Func<Model, T>> function)
{
}
// Call it like this:
Analyze(m => new { m.IdOne, m.IdTwo });
Then I looked at the generated lambda in the debugger. This is what C# generates:
{m => new <>f__AnonymousType0'2(IdOne = m.IdOne, IdTwo = m.IdTwo)}
Doing the same thing on the F# side using the getFuncTree function from Tomas using <# fun (m : Model) -> ModelKey(m.IdOne, m.IdTwo) #> yields:
{m => new ModelKey(m.IdOne, m.IdTwo)}
As you can see, the explicit naming of the - what is this anyway, looks like properties - arguments is missing in the F# code. I recreated the whole expression tree by hand in F# so that it would look like the C# version:
let modelKeyExpression =
Expression.Lambda<Func<Model, ModelKey>> (
body = Expression.New (
``constructor`` = typeof<ModelKey>.GetConstructor [| typeof<Int32>; typeof<Int32> |],
arguments = seq {
yield Expression.MakeMemberAccess (
expression = Expression.Parameter (
``type`` = typeof<Model>,
name = "m"
),
``member`` = typeof<Model>.GetProperty "IdOne"
) :> Expression;
yield Expression.MakeMemberAccess (
expression = Expression.Parameter (
``type`` = typeof<Model>,
name = "m"
),
``member`` = typeof<Model>.GetProperty "IdTwo"
) :> Expression
},
members = seq {
yield (typeof<ModelKey>.GetProperty "IdOne") :> MemberInfo
yield (typeof<ModelKey>.GetProperty "IdTwo") :> MemberInfo
}
),
parameters = [
Expression.Parameter (
``type`` = typeof<Model>,
name = "m"
)
]
)
The part that was missing in the F# representation is the members sequence.
When I move the mouse over this expression, this representation appears:
{m => new ModelKey(IdOne = m.IdOne, IdTwo = m.IdTwo)}
As you can see, apart from the class, it looks the same. But when I try to use this expression in the HasKey method, I get the following InvalidOperationException:
The properties expression 'm => new ModelKey(IdOne = m.IdOne, IdTwo= m.IdTwo)'
is not valid. The expression should represent a property: C#: 't =>
t.MyProperty' VB.Net: 'Function(t) t.MyProperty'. When specifying multiple
properties use an anonymous type: C#: 't => new { t.MyProperty1,
t.MyProperty2 }' VB.Net: 'Function(t) New With { t.MyProperty1,
t.MyProperty2 }'.
So it seems to me that this anonymous class syntax does something special...

In F# 4.6, this has worked for me after much struggle.
Apperantly all you need is a tuple. Makes sense since an anon object without explicit member names is basically, a tuple.
I can't believe nobody has put this on the MS documentation tho.
modelBuilder.Entity<Entity>()
.HasKey(fun e -> (e.Key1, e.Key2) :> obj)
|> ignore

EDIT: The technique below would be needed in F# 2.0, but it should not be needed in the newer versions. There must be some other issue with the F#-generated expression tree...
I think the problem is that Entity Framework wants you to specify a lambda expression as an expression tree:
modelBuilder.Entity<Model>().HasKey(fun (m : Model) -> ModelKey (m.IdOne, m.IdTwo))
This should just work in F# 3.1 in Visual Studio 2013, but it is not supported in F# 3.0. You can still do this, but you'll have to use F# quotations and write a bit of code that converts a quotation to LINQ expression tree - there is a helper that does most of the work:
open System
open System.Linq.Expressions
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Linq.RuntimeHelpers
let getFuncTree (quotation:Expr<'a -> 'b>) =
let e = LeafExpressionConverter.QuotationToExpression quotation
let call = e :?> MethodCallExpression
let lam = call.Arguments.[0] :?> LambdaExpression
Expression.Lambda<Func<'a, 'b>>(lam.Body, lam.Parameters)
getFuncTree <# fun x -> x + 1 #>
Using this, you should be able to call:
modelBuilder.Entity<Model>().HasKey(getFuncTree <# fun (m : Model) ->
ModelKey (m.IdOne, m.IdTwo) #>)
You could define an extension method like HasKeyQuot that does this behind the cover, to make the code a bit nicer.

It looks like the issue is twofold:
F#'s compiler never sets the Members collection of a LINQ NewExpression during quotation translation, but this is used to mark the construction of an anonymous type, which EF expects.
EF is really picky: even in C#, doing m => new { A = m.IdOne, B = m.IdTwo } won't work - the anonymous type's property names must match the model's property names.
One way to work around the issue (which is probably overkill, but works) is to create a new type dynamically at runtime which has fields with the right names and then just use a tuple in the F# code:
open Quotations.Patterns
open Quotations.ExprShape
open System.Reflection
open System.Linq.Expressions
module AnonymousTypeFixer =
let private mb =
let ab = System.AppDomain.CurrentDomain.DefineDynamicAssembly(AssemblyName("codeGen"), Emit.AssemblyBuilderAccess.ReflectionOnly)
ab.DefineDynamicModule("codeGen")
let transform (Lambda(v, (NewTuple exprs)) : Quotations.Expr<'a -> 'b>) =
let newV = Expression.Variable(v.Type, v.Name)
let cvtExpr (PropertyGet(Some(Var v'), p, [])) =
assert (v = v')
Expression.Property(newV, p) :> Expression, p
let ty = mb.DefineType(v.Type.Name)
let ctor = ty.DefineConstructor(MethodAttributes.Public (*||| MethodAttributes.RTSpecialName ||| MethodAttributes.SpecialName*), CallingConventions.HasThis, exprs |> List.map (fun e -> e.Type) |> List.toArray)
ctor.GetILGenerator().Emit(Emit.OpCodes.Ret)
let fields =
[for (_, p) in exprs |> List.map cvtExpr ->
ty.DefineField(p.Name, p.PropertyType, FieldAttributes.Public) :> MemberInfo]
ty.CreateType()
let newE = Expression.New(ctor, exprs |> Seq.map (cvtExpr >> fst), fields)
Expression.Lambda<System.Func<'a, obj>>(newE, newV)
let mb = System.Data.Entity.DbModelBuilder()
mb.Entity<Model>().HasKey(AnonymousTypeFixer.transform <# fun (m:Model) -> m.IdOne, m.IdTwo #>)

// F# 3.0
open Microsoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter
// Regular type with properties IdOne & IdTwo.
type ModelKey (idOne, idTwo) =
member this.IdOne = idOne
member this.IdTwo = idTwo
modelBuilder.Entity<Model>()
.HasKey(QuotationToLambdaExpression(
<# Func<_,_>(fun m -> NewAnonymousObjectHelper<_>(ModelKey(m.IdOne, m.IdTwo))) #>
)
)

Related

Difficulty rewriting code from c# to f# related to Entity Framework

I have a block of code that I want to write in F#, but the examples I have are in C#. I would like some help to write this in the F# language, and help understanding how it works.
Here is the c# code I have to mimic:
builder.HasMany(r => r.Options).WithOne(o => o.Root).HasForeignKey(o => o.RootId).OnDelete(DeleteBehavior.Cascade);
In F#, I am trying to do this:
builder
.HasOne(fun i -> i.ProductionReport)
.WithMany(fun pr -> pr.CostItems)
.HasForeignKey(fun pr -> pr.ProductionReportId).OnDelete(DeleteBehavior.Cascade) |> ignore
And the issue, per visual studio, is that pr is of type obj. How do I make sure f# knows that pr is of type ProductionReport, according to the return type of builder.HasOne.
Here is the complete sample requested:
BackendDemoDbContext
namespace BackendDemo.BackendDemoContext
open Microsoft.EntityFrameworkCore
type BackendDemoContext(options: DbContextOptions<BackendDemoContext>) =
inherit DbContext(options)
override __.OnModelCreating modelbuilder =
//Todo:
//modelbuilder.ApplyConfiguration(new CostItemEntityTypeConfiguration());
//modelbuilder.ApplyConfiguration(new ProductionReportEntityTypeConfiguration());
CostItem
namespace BackendDemo.Data.Models
type CostItem() =
member val CostItemId = null with get, set
member val Paper1 = null with get, set
member val Paper2 = null with get, set
member val Cases = null with get, set
member val Boxes = null with get, set
member val Paste = null with get, set
member val Bundling = null with get, set
member val Ink = null with get, set
member val Cardboard = null with get, set
member val Wrapping = null with get, set
member val Labour = null with get, set
member val Fringe = null with get, set
member val Pallet = null with get, set
member val ProductionReportId =null with get,set
member val ProductionReport = null with get, set
ProductionReport
namespace BackendDemo.Data.Models
open System.Collections
open BackendDemo.Data.Models
type ProductionReport() =
//val keyword necessary for AutoProperties
member val ProductionReportId : int = 2
//Todo:
//abstract member CostItems : ICollection<CostItem> with get, set
CostItemEntityTypeConfiguration
namespace BackendDemo.Data.EntityConfigurations
open Microsoft.EntityFrameworkCore
open Microsoft.EntityFrameworkCore.Metadata.Builders
open BackendDemo.Data.Models
type CostItemEntityTypeConfiguration =
interface IEntityTypeConfiguration<CostItem> with
override this.Configure(builder: EntityTypeBuilder<CostItem>) =
builder.ToTable("CostItem") |> ignore
builder.HasKey(fun i -> i.CostItemId) |> ignore
builder.Property(fun i -> i.Paper1).IsRequired() |> ignore
builder.Property(fun i -> i.Paper2).IsRequired() |> ignore
builder.Property(fun i -> i.Cases).IsRequired() |> ignore
builder.Property(fun i -> i.Boxes).IsRequired() |> ignore
builder.Property(fun i -> i.Paste).IsRequired() |> ignore
builder.Property(fun i -> i.Bundling).IsRequired() |> ignore
builder.Property(fun i -> i.Ink).IsRequired() |> ignore
builder.Property(fun i -> i.Cardboard).IsRequired() |> ignore
builder.Property(fun i -> i.Wrapping).IsRequired() |> ignore
builder.Property(fun i -> i.Labour).IsRequired() |> ignore
builder.Property(fun i -> i.Fringe).IsRequired() |> ignore
builder.Property(fun i -> i.Pallet).IsRequired() |> ignore
builder
.HasOne(fun i -> i.ProductionReport)
.WithMany(fun pr -> pr.CostItems)
.HasForeignKey(fun pr -> pr.ProductionReportId).OnDelete(DeleteBehavior.Cascade) |> ignore
ProductionReportEntityTypeConfiguration
namespace BackendDemo.Data.EntityConfigurations
open Microsoft.EntityFrameworkCore
open Microsoft.EntityFrameworkCore.Metadata.Builders
open BackendDemo.Data.Models
type ProductionReportEntityTypeConfiguration =
interface IEntityTypeConfiguration<ProductionReport> with
override this.Configure(builder: EntityTypeBuilder<ProductionReport>) =
builder.ToTable("ProductionReport") |> ignore
//Todo
///builder.HasKey(fun r -> r.ProductionReportId) |> ignore
Here are the results of the suggestions below (thanks by the way!):
1 Try forcing an argument type
builder
.HasOne(fun i -> i.ProductionReport)
.WithMany(fun (pr: ProductionReport) -> pr.CostItems)
Result
2 Use the alternative Function syntax
builder
.HasOne(<# fun i -> i.ProductionReport #>)
.WithMany(<# fun pr -> pr.CostItems #>)
Result
3 Use the <# notation with specific type
builder
.HasOne(<# Func<ProductionReport,_> fun i -> i.ProductionReport #>)
.WithMany(<# Func<CostItem,_> fun pr -> pr.CostItems #>)
Result
4 Factorize the Expression solution from Nathan
static member toExpr (f:'a -> 'b) =
<# Func<_,_> (f) #>
|> LeafExpressionConverter.QuotationToExpression
|> unbox<Expression<Func<'a, 'b>>>
Factorization class
Result
5 Factorize the Expression with type notation suggested by Nathan
static member toExpr<'a, 'b> (f:'a -> 'b) =
<# Func<_,_> (f) #>
|> LeafExpressionConverter.QuotationToExpression
|> unbox<Expression<Func<'a, 'b>>>
Result
I think I got it, but it took some digging to figure out how to work with the expressions. I referenced this post's history to see how to build a System.Linq.Expressions.Expression. Here's what I have:
open System.Linq.Expressions
open Microsoft.FSharp.Linq.RuntimeHelpers
...
let toProdRptExpr : Expression<Func<CostItem, ProductionReport>> =
<# Func<_, _> (fun (i:CostItem) -> i.ProductionReport) #>
|> LeafExpressionConverter.QuotationToExpression
|> unbox<Expression<Func<CostItem, ProductionReport>>>
let toCostItemsExpr : Expression<Func<ProductionReport, seq<CostItem>>> =
<# Func<_,_> (fun (pr:ProductionReport) -> pr.CostItems) #>
|> LeafExpressionConverter.QuotationToExpression
|> unbox<Expression<Func<ProductionReport, seq<CostItem>>>>
let a = builder.HasOne(toProdRptExpr)
let b = a.WithMany(toCostItemsExpr)
that's a lot more verbose than it needs to be, but it helped me figure out how the types fit together.
EDIT
For brevity, you can create a function like
let toExpr (f:'a -> 'b) =
<# Func<_,_> (f) #>
|> LeafExpressionConverter.QuotationToExpression
|> unbox<Expression<Func<'a, 'b>>>
and then use it like
builder
.HasOne(toExpr(fun (i:CostItem) -> i.ProductionReport))
.WithMany(toExpr(fun (pr:ProductionReport) -> pr.CostItems))
But you have to be careful because it looks like CostItem and ProductionReport are mutually referential (see the discussion in comments below). That means they need to be defined in the same file and use the and keyword (see this example)

Passing function arguments by Name Scala

I have the following scala code. In this code, I am passing a (global) string name to a function and want to change the string depending on the first argument as shown below:
def retVal(x: (String,String), y: => String) = {if (x._1 != "") {y = x._1;x} else (y,x._2)}
But when I run this code, I get the following error:
y = x._1
^
reassignment to a val
How can I modify the code, so that I get the global string variable get updated when I call this function?
Function arguments are by default immutable in Scala. You cannot assign the a value to the function parameter.
In your case you are trying to assign to a call by name param which is not at all possible.
Also mutating is bad instead return the value and assign it to a new variable.
But still if you want to mutate do something like this
object MutationBox {
var globalString = ""
def retVal(x: (String,String)) = {
if (x._1.nonEmpty) {
globalString = x._1
x
} else (globalString, x._2)
}
}

What scala expects as a value when it's method accepts '?0' as argument type?

I want to pass a String type argument in a setValue() of vaadin with scala.
Problem :
it shows like def setValue(x$1: ?0): Unit. it means it is expecting some ?0 type of arg.
i don't know how to handle this.
It will be nice if anyone can explain what is this type exactly, what kind of value it accepts and how can I pass String type arg to that method.
Note : setValue(Object newValue) works fine with java.
Here is the code snippet.
def getProcessTreeContainer(): HierarchicalContainer = {
var container = new HierarchicalContainer();
container.addContainerProperty("process", classOf[java.lang.String], null)
val tc = new TableCommon();
var process_menu_data_object_list = tc.getProcessTree();
val size = process_menu_data_object_list.size()
val obj = process_menu_data_object_list.iterator()
while (obj.hasNext()) {
val key = obj.next().id
val parent_key = obj.next().family_id
var name = ""
if (key == parent_key) {
val l = obj.next().name
//println(l.toString()+"...at step 1")
println(("okiiess".asInstanceOf[String]))
var child: Item = container.getItem(container.addItem(key))
child.getItemProperty("process").setValue(l.asInstanceOf)
// arg l.asInstanceOf(), what I am passing in setValue() method, throws NullPointerException.
} else {
container.setParent(key, parent_key)
}
//println("okay...")
}
return container;
}
?0 is not a datatype, it's Scala compiler telling you it doesn't know what the type is. The issue is that child.getItemProperty("process") returns a raw type Property for some reason, which aren't supported in Scala and shouldn't be used in Java either (it should return Property<?> instead). Cast it to Property[String], since you know what its type actually is.

Deconstructing a list of parameters in class constructor - Coffeescript

If I have a class that I pass a number of parameters to:
class Foo
constructor: (parameters) ->
#bar = parameters.bar
#moo = parameters.moo
The class is created like so:
foo = new Foo(bar: 2, moo: 8)
My question is what is the most elegant way to detect in the constructor if the variables being passed exist, and if not to set a default. The way I would do it in javascript would be:
this.bar = ( parameters.bar !== undefined ) ? parameters.bar : 10;
where 10 is the default.
Thanks for your help :)
Good answers - Just to summerize the best:
Inorder to detect if a parameter exists and define a default if it doesn't, in javascript is:
this.bar = ( parameters.bar !== undefined ) ? parameters.bar : 10;
and in coffescript is:
#bar = parameters.bar ? 10
So elegant and compact!
You can use the existential operator:
class Foo
constructor: (options = {}) ->
#bar = options.bar ? 10
#moo = options.moo ? 20
That constructor compiles to:
// Compiled JS.
function Foo(options) {
var _ref, _ref1;
if (options == null) {
options = {};
}
this.bar = (_ref = options.bar) != null ? _ref : 10;
this.moo = (_ref1 = options.moo) != null ? _ref1 : 20;
}
An equivalent alternative is to destruct the options object immediately (therefore avoiding an unnecessary name for it) and then setting the default values in case those options were not passed:
class Foo
constructor: ({#bar, #moo} = {}) ->
#bar ?= 10
#moo ?= 20
As passing an object with options is a common pattern in JS code, some libraries have useful utility functions that can help with this. Underscore, for example, provides _.defaults, which i think results in quite readable code:
class Foo
constructor: ({#bar, #moo} = {}) ->
_.defaults #, bar: 10, moo: 20
If you are not using Underscore, there is also $.extend (who doesn't use jQuery anyway?):
class Foo
defaults = bar: 10, moo: 20
constructor: (options = {}) ->
{#bar, #moo} = $.extend {}, defaults, options
An alternative is to extend the Foo object directly if you trust that the only options that will be passed are the valid ones (this results in quite minimal JS compared to the other ones):
class Foo
defaults = bar: 10, moo: 20
constructor: (options = {}) ->
$.extend #, defaults, options
And a final alternative is to have the default values in the Foo.prototype and only set them as own properties if they come in the options parameter:
class Foo
bar: 10
moo: 20
constructor: ({bar, moo} = {}) ->
#bar = bar if bar?
#moo = moo if moo?
This prevents all the instances of Foo from having separate properties of their own and instead shares the same properties between instances when they use the default values, the same that is usually done with methods. You can also do #bar = bar if #bar isnt bar to assign those properties only when the parameter value differs from the default value.
As you can see, there are quite a lot of ways to do this. None of them is perfect, they all have their pros and cons, so try to choose the one that better suits your needs/taste/whatever =D
You could do this using the ?= operator:
class Foo
constructor: (parameters) ->
#bar = parameters.bar
#bar ?= 10
#moo = parameters.moo
#moo ?= 20
If you didn't mind passing in positional arguments rather than a hash, you could also do this very elegantly using default parameter values:
class Foo
constructor: (#bar = 10, #moo = 20) ->
f = new Foo
You could define an object of defaults and do something like this..
(parameters = {}) ->
this[key] = parameters[key] ? defaults[key] for key, value of defaults
But you really don't have to go through all of that. The easiest way to make default values is just to use prototypal inheritance...
class Foo
bar: 10
moo: 10
constructor: (parameters = {}) ->
this[key] = value for own key, value of parameters
Assuming the above:
a = new Foo();
a.bar # => 10
b = new Foo(bar: 50)
b.bar # => 50
With the exception of the constructor every property you define with a : will become a property on the object's prototype.
The class definition translates to this JavaScript:
Foo = (function() {
Foo.prototype.bar = 10;
Foo.prototype.moo = 10;
//etcetc
return Foo;
})();
Play with it. Hope that helps.
There is a more simpler way:
class Foo
constructor: (parameters = {}) ->
{
#bar = 10
#moo = 20
} = parameters
The new version of coffeescript has a great way to solve this exact problem
class MyObject
constructor: ({string1='x', string2='y'} = {})
it works great with regular functions too
myFunction = ({string1='x', string2='y'} = {}) ->
string1 + string2
console.log myFunction({string1:"a", string2:"b"})
# outputs: 'ab'
console.log myFunction()
# outputs: 'xy'

Scala Graph Cycle Detection Algo 'return' needed?

I have implemented a small cycle detection algorithm for a DAG in Scala.
The 'return' bothers me - I'd like to have a version without the return...possible?
def isCyclic() : Boolean = {
lock.readLock().lock()
try {
nodes.foreach(node => node.marker = 1)
nodes.foreach(node => {if (1 == node.marker && visit(node)) return true})
} finally {
lock.readLock().unlock()
}
false
}
private def visit(node: MyNode): Boolean = {
node.marker = 3
val nodeId = node.id
val children = vertexMap.getChildren(nodeId).toList.map(nodeId => id2nodeMap(nodeId))
children.foreach(child => {
if (3 == child.marker || (1 == child.marker && visit(child))) return true
})
node.marker = 2
false
}
Yes, by using '.find' instead of 'foreach' + 'return':
http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.Seq
def isCyclic() : Boolean = {
def visit(node: MyNode): Boolean = {
node.marker = 3
val nodeId = node.id
val children = vertexMap.getChildren(nodeId).toList.map(nodeId => id2nodeMap(nodeId))
val found = children.exists(child => (3 == child.marker || (1 == child.marker && visit(child))))
node.marker = 2
found
}
lock.readLock().lock()
try {
nodes.foreach(node => node.marker = 1)
nodes.exists(node => node.marker && visit(node))
} finally {
lock.readLock().unlock()
}
}
Summary:
I have originated two solutions as generic FP functions which detect cycles within a directed graph. And per your implied preference, the use of an early return to escape the recursive function has been eliminated. The first, isCyclic, simply returns a Boolean as soon as the DFS (Depth First Search) repeats a node visit. The second, filterToJustCycles, returns a copy of the input Map filtered down to just the nodes involved in any/all cycles, and returns an empty Map when no cycles are found.
Details:
For the following, please Consider a directed graph encoded as such:
val directedGraphWithCyclesA: Map[String, Set[String]] =
Map(
"A" -> Set("B", "E", "J")
, "B" -> Set("E", "F")
, "C" -> Set("I", "G")
, "D" -> Set("G", "L")
, "E" -> Set("H")
, "F" -> Set("G")
, "G" -> Set("L")
, "H" -> Set("J", "K")
, "I" -> Set("K", "L")
, "J" -> Set("B")
, "K" -> Set("B")
)
In both functions below, the type parameter N refers to whatever "Node" type you care to provide. It is important the provided "Node" type be both immutable and have stable equals and hashCode implementations (all of which occur automatically with use of immutable case classes).
The first function, isCyclic, is a similar in nature to the version of the solution provided by #the-archetypal-paul. It assumes the directed graph has been transformed into a Map[N, Set[N]] where N is the identity of a node in the graph.
If you need to see how to generically transform your custom directed graph implementation into a Map[N, Set[N]], I have outlined a generic solution towards the end of this answer.
Calling the isCyclic function as such:
val isCyclicResult = isCyclic(directedGraphWithCyclesA)
will return:
`true`
No further information is provided. And the DFS (Depth First Search) is aborted at detection of the first repeated visit to a node.
def isCyclic[N](nsByN: Map[N, Set[N]]) : Boolean = {
def hasCycle(nAndNs: (N, Set[N]), visited: Set[N] = Set[N]()): Boolean =
if (visited.contains(nAndNs._1))
true
else
nAndNs._2.exists(
n =>
nsByN.get(n) match {
case Some(ns) =>
hasCycle((n, ns), visited + nAndNs._1)
case None =>
false
}
)
nsByN.exists(hasCycle(_))
}
The second function, filterToJustCycles, uses the set reduction technique to recursively filter away unreferenced root nodes in the Map. If there are no cycles in the supplied graph of nodes, then .isEmpty will be true on the returned Map. If however, there are any cycles, all of the nodes required to participate in any of the cycles are returned with all of the other non-cycle participating nodes filtered away.
Again, if you need to see how to generically transform your custom directed graph implementation into a Map[N, Set[N]], I have outlined a generic solution towards the end of this answer.
Calling the filterToJustCycles function as such:
val cycles = filterToJustCycles(directedGraphWithCyclesA)
will return:
Map(E -> Set(H), J -> Set(B), B -> Set(E), H -> Set(J, K), K -> Set(B))
It's trivial to then create a traversal across this Map to produce any or all of the various cycle pathways through the remaining nodes.
def filterToJustCycles[N](nsByN: Map[N, Set[N]]): Map[N, Set[N]] = {
def recursive(nsByNRemaining: Map[N, Set[N]], referencedRootNs: Set[N] = Set[N]()): Map[N, Set[N]] = {
val (referencedRootNsNew, nsByNRemainingNew) = {
val referencedRootNsNewTemp =
nsByNRemaining.values.flatten.toSet.intersect(nsByNRemaining.keySet)
(
referencedRootNsNewTemp
, nsByNRemaining.collect {
case (t, ts) if referencedRootNsNewTemp.contains(t) && referencedRootNsNewTemp.intersect(ts.toSet).nonEmpty =>
(t, referencedRootNsNewTemp.intersect(ts.toSet))
}
)
}
if (referencedRootNsNew == referencedRootNs)
nsByNRemainingNew
else
recursive(nsByNRemainingNew, referencedRootNsNew)
}
recursive(nsByN)
}
So, how does one generically transform a custom directed graph implementation into a Map[N, Set[N]]?
In essence, "Go Scala case classes!"
First, let's define an example case of a real node in a pre-existing directed graph:
class CustomNode (
val equipmentIdAndType: String //"A387.Structure" - identity is embedded in a string and must be parsed out
, val childrenNodes: List[CustomNode] //even through Set is implied, for whatever reason this implementation used List
, val otherImplementationNoise: Option[Any] = None
)
Again, this is just an example. Yours could involve subclassing, delegation, etc. The purpose is to have access to a something that will be able to fetch the two essential things to make this work:
the identity of a node; i.e. something to distinguish it and makes it unique from all other nodes in the same directed graph
a collection of the identities of the immediate children of a specific node - if the specific node doesn't have any children, this collection will be empty
Next, we define a helper object, DirectedGraph, which will contain the infrastructure for the conversion:
Node: an adapter trait which will wrap CustomNode
toMap: a function which will take a List[CustomNode] and convert it to a Map[Node, Set[Node]] (which is type equivalent to our target type of Map[N, Set[N]])
Here's the code:
object DirectedGraph {
trait Node[S, I] {
def source: S
def identity: I
def children: Set[I]
}
def toMap[S, I, N <: Node[S, I]](ss: List[S], transformSToN: S => N): Map[N, Set[N]] = {
val (ns, nByI) = {
val iAndNs =
ss.map(
s => {
val n =
transformSToN(s)
(n.identity, n)
}
)
(iAndNs.map(_._2), iAndNs.toMap)
}
ns.map(n => (n, n.children.map(nByI(_)))).toMap
}
}
Now, we must generate the actual adapter, CustomNodeAdapter, which will wrap each CustomNode instance. This adapter uses a case class in a very specific way; i.e. specifying two constructor parameters lists. It ensures the case class conforms to a Set's requirement that a Set member have correct equals and hashCode implementations. For more details on why and how to use a case class this way, please see this StackOverflow question and answer:
object CustomNodeAdapter extends (CustomNode => CustomNodeAdapter) {
def apply(customNode: CustomNode): CustomNodeAdapter =
new CustomNodeAdapter(fetchIdentity(customNode))(customNode) {}
def fetchIdentity(customNode: CustomNode): String =
fetchIdentity(customNode.equipmentIdAndType)
def fetchIdentity(eiat: String): String =
eiat.takeWhile(char => char.isLetter || char.isDigit)
}
abstract case class CustomNodeAdapter(identity: String)(customNode: CustomNode) extends DirectedGraph.Node[CustomNode, String] {
val children =
customNode.childrenNodes.map(CustomNodeAdapter.fetchIdentity).toSet
val source =
customNode
}
We now have the infrastructure in place. Let's define a "real world" directed graph consisting of CustomNode:
val customNodeDirectedGraphWithCyclesA =
List(
new CustomNode("A.x", List("B.a", "E.a", "J.a"))
, new CustomNode("B.x", List("E.b", "F.b"))
, new CustomNode("C.x", List("I.c", "G.c"))
, new CustomNode("D.x", List("G.d", "L.d"))
, new CustomNode("E.x", List("H.e"))
, new CustomNode("F.x", List("G.f"))
, new CustomNode("G.x", List("L.g"))
, new CustomNode("H.x", List("J.h", "K.h"))
, new CustomNode("I.x", List("K.i", "L.i"))
, new CustomNode("J.x", List("B.j"))
, new CustomNode("K.x", List("B.k"))
, new CustomNode("L.x", Nil)
)
Finally, we can now do the conversion which looks like this:
val transformCustomNodeDirectedGraphWithCyclesA =
DirectedGraph.toMap[CustomNode, String, CustomNodeAdapter](customNodes1, customNode => CustomNodeAdapter(customNode))
And we can take transformCustomNodeDirectedGraphWithCyclesA, which is of type Map[CustomNodeAdapter,Set[CustomNodeAdapter]], and submit it to the two original functions.
Calling the isCyclic function as such:
val isCyclicResult = isCyclic(transformCustomNodeDirectedGraphWithCyclesA)
will return:
`true`
Calling the filterToJustCycles function as such:
val cycles = filterToJustCycles(transformCustomNodeDirectedGraphWithCyclesA)
will return:
Map(
CustomNodeAdapter(B) -> Set(CustomNodeAdapter(E))
, CustomNodeAdapter(E) -> Set(CustomNodeAdapter(H))
, CustomNodeAdapter(H) -> Set(CustomNodeAdapter(J), CustomNodeAdapter(K))
, CustomNodeAdapter(J) -> Set(CustomNodeAdapter(B))
, CustomNodeAdapter(K) -> Set(CustomNodeAdapter(B))
)
And if needed, this Map can then be converted back to Map[CustomNode, List[CustomNode]]:
cycles.map {
case (customNodeAdapter, customNodeAdapterChildren) =>
(customNodeAdapter.source, customNodeAdapterChildren.toList.map(_.source))
}
If you have any questions, issues or concerns, please let me know and I will address them ASAP.
I think the problem can be solved without changing the state of the node with the marker field. The following is a rough code of what i think the isCyclic should look like. I am currently storing the node objects which are visited instead you can store the node ids if the node doesnt have equality based on node id.
def isCyclic() : Boolean = nodes.exists(hasCycle(_, HashSet()))
def hasCycle(node:Node, visited:Seq[Node]) = visited.contains(node) || children(node).exists(hasCycle(_, node +: visited))
def children(node:Node) = vertexMap.getChildren(node.id).toList.map(nodeId => id2nodeMap(nodeId))
Answer added just to show that the mutable-visited isn't too unreadable either (untested, though!)
def isCyclic() : Boolean =
{
var visited = HashSet()
def hasCycle(node:Node) = {
if (visited.contains(node)) {
true
} else {
visited :+= node
children(node).exists(hasCycle(_))
}
}
nodes.exists(hasCycle(_))
}
def children(node:Node) = vertexMap.getChildren(node.id).toList.map(nodeId => id2nodeMap(nodeId))
If p = node => node.marker==1 && visit(node) and assuming nodes is a List you can pick any of the following:
nodes.filter(p).length>0
nodes.count(p)>0
nodes.exists(p) (I think the most relevant)
I am not sure of the relative complexity of each method and would appreciate a comment from fellow members of the community