Skip to content

Commit 0d4a2da

Browse files
committed
some new combinators
1 parent 99682f4 commit 0d4a2da

4 files changed

Lines changed: 133 additions & 5 deletions

File tree

RELEASE_NOTES.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
### 1.2.10
2+
* added `HashSet.ChooseToMap(V)`
3+
* added `ASet.sort(By)Descending`
4+
* added `AMap.sortBy(Descending)`
5+
16
### 1.2.10
27
* added `clist.UpdateTo(seq, list, array)`
38
* fixed AssemblyVersions

src/FSharp.Data.Adaptive/CollectionExtensions.fs

Lines changed: 104 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,12 @@ module CollectionExtensions =
3737
module internal Readers =
3838
/// Reader for ASet.sortBy
3939
[<Sealed>]
40-
type SetSortByReader<'T1, 'T2 when 'T2 : comparison>(set: aset<'T1>, projection: 'T1 -> 'T2) =
40+
type SetSortByReader<'T1, 'T2 when 'T2 : comparison>(set: aset<'T1>, projection: 'T1 -> 'T2, cmp : System.Collections.Generic.IComparer<'T2>) =
4141
inherit AbstractReader<IndexListDelta<'T1>>(IndexListDelta.empty)
4242

4343
let reader = set.GetReader()
4444
let mapping = IndexMapping<Unique<'T2>>()
45-
let cache = Cache<'T1, Unique<'T2>>(projection >> Unique)
45+
let cache = Cache<'T1, Unique<'T2>>(fun v -> Unique(projection v, cmp))
4646

4747
override x.Compute(token: AdaptiveToken) =
4848
reader.GetChanges token |> Seq.choose (fun op ->
@@ -200,6 +200,56 @@ module CollectionExtensions =
200200
|> IndexListDelta.toSeq
201201
|> HashMapDelta.ofSeq
202202

203+
[<Sealed>]
204+
type MapSortByReader<'K, 'V, 'T when 'T : comparison>(map : amap<'K, 'V>, cmp : System.Collections.Generic.IComparer<'T>, projection : OptimizedClosures.FSharpFunc<'K, 'V, 'T>) =
205+
inherit AbstractReader<IndexListDelta<'K * 'V>>(IndexListDelta.empty)
206+
207+
let reader = map.GetReader()
208+
209+
let mapping =
210+
CustomIndexMapping<int * 'T>(fun (k0, t0) (k1, t1) ->
211+
let c = cmp.Compare(t0, t1)
212+
if c <> 0 then c
213+
else compare k0 k1
214+
)
215+
let mutable state = HashMap.empty<'K, int * 'T>
216+
let mutable currentId = 0
217+
218+
let newId() =
219+
let i = currentId
220+
currentId <- i + 1
221+
i
222+
223+
override x.Compute(token : AdaptiveToken) =
224+
let ops = reader.GetChanges token
225+
let mutable res = MapExt.empty
226+
for key, op in ops do
227+
match op with
228+
| Set v ->
229+
let id =
230+
match HashMap.tryFind key state with
231+
| Some (i, _) -> i
232+
| None -> newId()
233+
234+
let t = projection.Invoke(key, v)
235+
let index = mapping.Invoke(id, t)
236+
res <- MapExt.add index (Set (key, v)) res
237+
state <- HashMap.add key (id, t) state
238+
| Remove ->
239+
match HashMap.tryRemove key state with
240+
| Some ((id, t), rest) ->
241+
state <- rest
242+
match mapping.Revoke(id, t) with
243+
| Some index ->
244+
res <- MapExt.add index (Remove) res
245+
| None ->
246+
()
247+
| None ->
248+
()
249+
250+
IndexListDelta(res)
251+
252+
203253

204254
/// Functional operators for amap<_,_>
205255
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
@@ -249,6 +299,37 @@ module CollectionExtensions =
249299
ListToMapReader(list) :> _
250300
}
251301

302+
/// Creates a sorted alist holding Key/Value tuples from the amap using the given projection.
303+
let sortBy (projection : 'K -> 'V -> 'T) (map : amap<'K, 'V>) : alist<'K * 'V> =
304+
if map.IsConstant then
305+
map
306+
|> AMap.force
307+
|> HashMap.toList
308+
|> List.sortBy (fun (k, v) -> projection k v)
309+
|> AList.ofList
310+
else
311+
AList.ofReader <| fun () ->
312+
MapSortByReader(map, LanguagePrimitives.FastGenericComparer, OptimizedClosures.FSharpFunc<_,_,_>.Adapt projection)
313+
314+
/// Creates a sorted (descending order) alist holding Key/Value tuples from the amap using the given projection.
315+
let sortByDescending (projection : 'K -> 'V -> 'T) (map : amap<'K, 'V>) : alist<'K * 'V> =
316+
if map.IsConstant then
317+
map
318+
|> AMap.force
319+
|> HashMap.toList
320+
|> List.sortByDescending (fun (k, v) -> projection k v)
321+
|> AList.ofList
322+
else
323+
AList.ofReader <| fun () ->
324+
let cmp =
325+
let c = LanguagePrimitives.FastGenericComparer
326+
{ new System.Collections.Generic.IComparer<'T> with
327+
member x.Compare(a, b) = c.Compare(b,a)
328+
}
329+
MapSortByReader(map, cmp, OptimizedClosures.FSharpFunc<_,_,_>.Adapt projection)
330+
331+
332+
252333

253334
/// Functional operators for aset<_>
254335
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
@@ -302,11 +383,31 @@ module CollectionExtensions =
302383
|> Seq.map snd
303384
|> AList.ofSeq
304385
else
305-
AList.ofReader (fun () -> SetSortByReader(set, projection))
386+
AList.ofReader (fun () -> SetSortByReader(set, projection, LanguagePrimitives.FastGenericComparer<'T2>))
306387

307388
/// Sorts the set.
308389
let inline sort (set: aset<'T>) = sortWith compare set
309390

391+
/// Sorts the set in descending order.
392+
let inline sortDescending (set: aset<'T>) = sortWith (fun a b -> compare b a) set
393+
394+
/// Sorts the set using the keys given by projection in descending order.
395+
let sortByDescending (projection: 'T1 -> 'T2) (set: aset<'T1>) =
396+
if set.IsConstant then
397+
set.Content
398+
|> AVal.force
399+
|> Seq.indexed
400+
|> Seq.sortByDescending (fun (i,v) -> projection v, -i)
401+
|> Seq.map snd
402+
|> AList.ofSeq
403+
else
404+
let cmp =
405+
let c = LanguagePrimitives.FastGenericComparer<'T2>
406+
{ new System.Collections.Generic.IComparer<'T2> with
407+
member x.Compare(a,b) = c.Compare(b,a)
408+
}
409+
AList.ofReader (fun () -> SetSortByReader(set, projection, cmp))
410+
310411
/// Creates an alist from the set with undefined element order.
311412
let toAList (set: aset<'T>) =
312413
if set.IsConstant then

src/FSharp.Data.Adaptive/Datastructures/HashCollections.fs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3241,6 +3241,23 @@ type HashSet<'K> internal(comparer : IEqualityComparer<'K>, root : SetNode<'K>)
32413241
let root = SetNode.mapToMap mapping root
32423242
HashMap<'K, 'V>(comparer, root)
32433243

3244+
3245+
[<MethodImpl(MethodImplOptions.AggressiveInlining)>]
3246+
member x.ChooseToMap(mapping : 'K -> option<'V>) =
3247+
let vmapping v =
3248+
match mapping v with
3249+
| Some v -> ValueSome v
3250+
| None -> ValueNone
3251+
3252+
let root = SetNode.chooseToMapV vmapping root
3253+
HashMap<'K, 'V>(comparer, root)
3254+
3255+
[<MethodImpl(MethodImplOptions.AggressiveInlining)>]
3256+
member x.ChooseToMapV(mapping : 'K -> voption<'V>) =
3257+
let root = SetNode.chooseToMapV mapping root
3258+
HashMap<'K, 'V>(comparer, root)
3259+
3260+
32443261
[<MethodImpl(MethodImplOptions.AggressiveInlining)>]
32453262
member x.ChooseV(mapping : 'K -> voption<'T>) =
32463263
let cmp = DefaultEqualityComparer<'T>.Instance

src/FSharp.Data.Adaptive/Utilities/Utilities.fs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ module internal AdaptiveIndexListHelpers =
269269

270270
new(f : Index -> 'a -> 'b) = IndexCache(f, ignore)
271271

272-
type Unique<'b when 'b : comparison>(value : 'b) =
272+
type Unique<'b>(value : 'b, cmp : IComparer<'b>) =
273273
static let mutable currentId = 0
274274
static let newId() =
275275
#if FABLE_COMPILER
@@ -295,11 +295,16 @@ module internal AdaptiveIndexListHelpers =
295295
member x.CompareTo o =
296296
match o with
297297
| :? Unique<'b> as o ->
298-
let c = compare value o.Value
298+
let c = cmp.Compare(value, o.Value)
299299
if c = 0 then compare id o.Id
300300
else c
301301
| _ ->
302302
failwith "uncomparable"
303+
member x.CompareTo (o : Unique<'b>) =
304+
let c = cmp.Compare(value, o.Value)
305+
if c = 0 then compare id o.Id
306+
else c
307+
303308

304309

305310
module internal RangeDelta =

0 commit comments

Comments
 (0)