Popular New Releases in Functional Programming
ramda
v0.28.0
mostly-adequate-guide
c5844c2
scala
Scala 2.13.8
fantasy-land
Version 5.0.0
fp-ts
2.11.10
Popular Libraries in Functional Programming
by ramda javascript
21915 MIT
:ram: Practical functional Javascript
by MostlyAdequate javascript
20991 NOASSERTION
Mostly adequate guide to FP (in javascript)
by scala scala
13628 Apache-2.0
Scala 2 compiler and standard library. For bugs, see scala/bug
by thoughtbot ruby
8955
A guide for programming in style.
by fantasyland javascript
8895 MIT
Specification for interoperability of common algebraic structures in JavaScript
by DeathKing ruby
8398
MIT视频公开课《计算机程序的构造和解释》中文化项目及课程学习资料搜集。
by lk-geimfari python
7943 NOASSERTION
A list of cool open source projects written in C, C++, Clojure, Lisp, Elixir, Erlang, Elm, Golang, Haskell, JavaScript, Lua, OCaml, Python, R, Ruby, Rust, Scala, etc.
by gcanti typescript
7696 MIT
Functional programming in TypeScript
by baconjs typescript
6398 MIT
Functional reactive programming library for TypeScript and JavaScript
Trending New libraries in Functional Programming
by gvergnaud typescript
2569 MIT
🎨 The exhaustive Pattern Matching library for TypeScript, with smart type inference.
by pancakeswap typescript
1679 GPL-3.0
:pancakes: Pancake main features (farms, pools, IFO, lottery, profiles)
by getify javascript
884 MIT
The most powerful IO monad implementation in JS, possibly in any language!
by idisfkj kotlin
775 Apache-2.0
🔥The Android Startup library provides a straightforward, performant way to initialize components at the application startup. Both library developers and app developers can use Android Startup to streamline startup sequences and explicitly set the order of initialization.
by CleanCut rust
554 MIT
Rust Programming Fundamentals - one course to rule them all, one course to find them...
by Hirrolot c
518 MIT
Algebraic data types for C99
by Hirrolot c
432 MIT
Full-blown preprocessor metaprogramming
by mobily typescript
392 MIT
🔧 Fast, modern, and practical utility library for FP in TypeScript.
by niltok html
363 CC-BY-4.0
十分钟魔法练习
Top Authors in Functional Programming
1
27 Libraries
554
2
18 Libraries
10630
3
17 Libraries
12142
4
16 Libraries
93
5
16 Libraries
1624
6
15 Libraries
919
7
12 Libraries
509
8
11 Libraries
3002
9
11 Libraries
5214
10
10 Libraries
602
1
27 Libraries
554
2
18 Libraries
10630
3
17 Libraries
12142
4
16 Libraries
93
5
16 Libraries
1624
6
15 Libraries
919
7
12 Libraries
509
8
11 Libraries
3002
9
11 Libraries
5214
10
10 Libraries
602
Trending Kits in Functional Programming
No Trending Kits are available at this moment for Functional Programming
Trending Discussions on Functional Programming
How do purely functional languages handle index-based algorithms?
Functional Programming: How does one create a new column to a dataframe that contains a multiindex column?
Functional Programming in Kotlin: Counting elements in list by using fold
Are java streams able to lazilly reduce from map/filter conditions?
Two recursive functions and stackoverflow errors in javascript/nodeJs. Understanding the differences
Using '|' (pipe) operator with std::views does not compile
Apply a filter on a multilevel object based on a different object
What is the relationship between monad functions dist and join in Haskell?
How to Log in F# in a functional way using NLog
Idiomatically moving/sorting values from one Vec into another
QUESTION
How do purely functional languages handle index-based algorithms?
Asked 2022-Apr-05 at 12:51I have been trying to learn about functional programming, but I still struggle with thinking like a functional programmer. One such hangup is how one would implement index-heavy operations which rely strongly on loops/order-of-execution.
For example, consider the following Java code:
1public class Main {
2 public static void main(String[] args) {
3 List<Integer> nums = Arrays.asList(1,2,3,4,5,6,7,8,9);
4 System.out.println("Nums:\t"+ nums);
5 System.out.println("Prefix:\t"+prefixList(nums));
6 }
7
8 private static List<Integer> prefixList(List<Integer> nums){
9 List<Integer> prefix = new ArrayList<>(nums);
10 for(int i = 1; i < prefix.size(); ++i)
11 prefix.set(i, prefix.get(i) + prefix.get(i-1));
12 return prefix;
13 }
14}
15/*
16System.out:
17Nums: [1, 2, 3, 4, 5, 6, 7, 8, 9]
18Prefix: [1, 3, 6, 10, 15, 21, 28, 36, 45]
19*/
20
Here, in the prefixList
function, the nums list is first cloned, but then there is the iterative operation performed on it, where the value on index i relies on index i-1 (i.e. order of execution is required). Then this value is returned.
What would this look like in a functional language (Haskell, Lisp, etc.)? I have been learning about monads and think they may be relevant here, but my understanding is still not great.
ANSWER
Answered 2022-Mar-07 at 21:17This is not an index-heavy operation, in fact you can do this with a one-liner with scanl1 :: (a -> a -> a) -> [a] -> [a]
:
1public class Main {
2 public static void main(String[] args) {
3 List<Integer> nums = Arrays.asList(1,2,3,4,5,6,7,8,9);
4 System.out.println("Nums:\t"+ nums);
5 System.out.println("Prefix:\t"+prefixList(nums));
6 }
7
8 private static List<Integer> prefixList(List<Integer> nums){
9 List<Integer> prefix = new ArrayList<>(nums);
10 for(int i = 1; i < prefix.size(); ++i)
11 prefix.set(i, prefix.get(i) + prefix.get(i-1));
12 return prefix;
13 }
14}
15/*
16System.out:
17Nums: [1, 2, 3, 4, 5, 6, 7, 8, 9]
18Prefix: [1, 3, 6, 10, 15, 21, 28, 36, 45]
19*/
20prefixList = scanl1 (+)
21
indeed, for the list of Nums
, we get:
1public class Main {
2 public static void main(String[] args) {
3 List<Integer> nums = Arrays.asList(1,2,3,4,5,6,7,8,9);
4 System.out.println("Nums:\t"+ nums);
5 System.out.println("Prefix:\t"+prefixList(nums));
6 }
7
8 private static List<Integer> prefixList(List<Integer> nums){
9 List<Integer> prefix = new ArrayList<>(nums);
10 for(int i = 1; i < prefix.size(); ++i)
11 prefix.set(i, prefix.get(i) + prefix.get(i-1));
12 return prefix;
13 }
14}
15/*
16System.out:
17Nums: [1, 2, 3, 4, 5, 6, 7, 8, 9]
18Prefix: [1, 3, 6, 10, 15, 21, 28, 36, 45]
19*/
20prefixList = scanl1 (+)
21Prelude> prefixList [1 .. 9]
22[1,3,6,10,15,21,28,36,45]
23
scanl1
takes the first item of the original list as initial value for the accumulator, and yields that. Then each time it takes the accumulator and the next item of the given list, and sums these up as new accumulator, and yields the new accumulator value.
Often one does not need indexing, but enumerating over the list is sufficient. Imperative programming languages often work with for
loops with indexes, but in many cases these can be replaced by foreach
loops that thus do not take the index into account. In Haskell this also often helps to make algorithms more lazy.
If you really need random access lookups, you can work with data structures such as defined in the array
and vector
packages.
QUESTION
Functional Programming: How does one create a new column to a dataframe that contains a multiindex column?
Asked 2022-Jan-28 at 03:46Suppose the below simplified dataframe. (The actual df is much, much bigger.) How does one assign values to a new column f
such that f
is a function of another column (e.,g. e
)? I'm pretty sure one needs to use apply
or map
but never done this with a dataframe that has multiindex columns?
1df = pd.DataFrame([[1,2,3,4], [5,6,7,8], [9,10,11,12], [13,14,15,16]])
2df.columns = pd.MultiIndex.from_tuples((("a", "d"), ("a", "e"), ("b", "d"), ("b","e")))
3df
4 a b
5 d e d e
60 1 2 3 4
71 5 6 7 8
82 9 10 11 12
93 13 14 15 16
10
Desired output:
1df = pd.DataFrame([[1,2,3,4], [5,6,7,8], [9,10,11,12], [13,14,15,16]])
2df.columns = pd.MultiIndex.from_tuples((("a", "d"), ("a", "e"), ("b", "d"), ("b","e")))
3df
4 a b
5 d e d e
60 1 2 3 4
71 5 6 7 8
82 9 10 11 12
93 13 14 15 16
10 a b
11 d e f d e f
120 1 2 1 3 4 1
131 5 6 1 7 8 -1
142 9 10 -1 11 12 -1
153 13 14 -1 15 16 -1
16
Would like to be able to apply the following lines and assign them to a new column f
. Two problems: First, the last line that contains the apply
doesn't work but hopefully my intent is clear. Second, I'm unsure how to assign values to a new column of a dataframe with a multi index column structure. Would like to be able use functional programming methods.
1df = pd.DataFrame([[1,2,3,4], [5,6,7,8], [9,10,11,12], [13,14,15,16]])
2df.columns = pd.MultiIndex.from_tuples((("a", "d"), ("a", "e"), ("b", "d"), ("b","e")))
3df
4 a b
5 d e d e
60 1 2 3 4
71 5 6 7 8
82 9 10 11 12
93 13 14 15 16
10 a b
11 d e f d e f
120 1 2 1 3 4 1
131 5 6 1 7 8 -1
142 9 10 -1 11 12 -1
153 13 14 -1 15 16 -1
16lt = df.loc(axis=1)[:,'e'] < 8
17gt = df.loc(axis=1)[:,'e'] >= 8
18conditions = [lt, gt]
19choices = [1, -1]
20df.loc(axis=1)[:,'f'] = df.loc(axis=1)[:,'e'].apply(np.select(conditions, choices))
21
ANSWER
Answered 2022-Jan-28 at 03:461df = pd.DataFrame([[1,2,3,4], [5,6,7,8], [9,10,11,12], [13,14,15,16]])
2df.columns = pd.MultiIndex.from_tuples((("a", "d"), ("a", "e"), ("b", "d"), ("b","e")))
3df
4 a b
5 d e d e
60 1 2 3 4
71 5 6 7 8
82 9 10 11 12
93 13 14 15 16
10 a b
11 d e f d e f
120 1 2 1 3 4 1
131 5 6 1 7 8 -1
142 9 10 -1 11 12 -1
153 13 14 -1 15 16 -1
16lt = df.loc(axis=1)[:,'e'] < 8
17gt = df.loc(axis=1)[:,'e'] >= 8
18conditions = [lt, gt]
19choices = [1, -1]
20df.loc(axis=1)[:,'f'] = df.loc(axis=1)[:,'e'].apply(np.select(conditions, choices))
21nms = [(i, 'f')for i, j in df.columns if j == 'e']
22df[nms] = (df.iloc[:, [j == 'e' for i, j in df.columns]] < 8) * 2 - 1
23
24df = df.sort_index(axis=1)
25df
26 a b
27 d e f d e f
280 1 2 1 3 4 1
291 5 6 1 7 8 -1
302 9 10 -1 11 12 -1
313 13 14 -1 15 16 -1
32
EDIT:
for a custom ordering:
1df = pd.DataFrame([[1,2,3,4], [5,6,7,8], [9,10,11,12], [13,14,15,16]])
2df.columns = pd.MultiIndex.from_tuples((("a", "d"), ("a", "e"), ("b", "d"), ("b","e")))
3df
4 a b
5 d e d e
60 1 2 3 4
71 5 6 7 8
82 9 10 11 12
93 13 14 15 16
10 a b
11 d e f d e f
120 1 2 1 3 4 1
131 5 6 1 7 8 -1
142 9 10 -1 11 12 -1
153 13 14 -1 15 16 -1
16lt = df.loc(axis=1)[:,'e'] < 8
17gt = df.loc(axis=1)[:,'e'] >= 8
18conditions = [lt, gt]
19choices = [1, -1]
20df.loc(axis=1)[:,'f'] = df.loc(axis=1)[:,'e'].apply(np.select(conditions, choices))
21nms = [(i, 'f')for i, j in df.columns if j == 'e']
22df[nms] = (df.iloc[:, [j == 'e' for i, j in df.columns]] < 8) * 2 - 1
23
24df = df.sort_index(axis=1)
25df
26 a b
27 d e f d e f
280 1 2 1 3 4 1
291 5 6 1 7 8 -1
302 9 10 -1 11 12 -1
313 13 14 -1 15 16 -1
32d = {i:j for j, i in enumerate(df.columns.levels[0])}
33df1 = df.loc[:, sorted(df.columns, key = lambda x: d[x[0]])]
34
IF the whole data is in a way symmetric, you could do:
1df = pd.DataFrame([[1,2,3,4], [5,6,7,8], [9,10,11,12], [13,14,15,16]])
2df.columns = pd.MultiIndex.from_tuples((("a", "d"), ("a", "e"), ("b", "d"), ("b","e")))
3df
4 a b
5 d e d e
60 1 2 3 4
71 5 6 7 8
82 9 10 11 12
93 13 14 15 16
10 a b
11 d e f d e f
120 1 2 1 3 4 1
131 5 6 1 7 8 -1
142 9 10 -1 11 12 -1
153 13 14 -1 15 16 -1
16lt = df.loc(axis=1)[:,'e'] < 8
17gt = df.loc(axis=1)[:,'e'] >= 8
18conditions = [lt, gt]
19choices = [1, -1]
20df.loc(axis=1)[:,'f'] = df.loc(axis=1)[:,'e'].apply(np.select(conditions, choices))
21nms = [(i, 'f')for i, j in df.columns if j == 'e']
22df[nms] = (df.iloc[:, [j == 'e' for i, j in df.columns]] < 8) * 2 - 1
23
24df = df.sort_index(axis=1)
25df
26 a b
27 d e f d e f
280 1 2 1 3 4 1
291 5 6 1 7 8 -1
302 9 10 -1 11 12 -1
313 13 14 -1 15 16 -1
32d = {i:j for j, i in enumerate(df.columns.levels[0])}
33df1 = df.loc[:, sorted(df.columns, key = lambda x: d[x[0]])]
34df.stack(0).assign(f = lambda x: 2*(x.e < 8) - 1).stack().unstack([1,2])
35Out[]:
36 a b
37 d e f d e f
380 1 2 1 3 4 1
391 5 6 1 7 8 -1
402 9 10 -1 11 12 -1
413 13 14 -1 15 16 -1
42
QUESTION
Functional Programming in Kotlin: Counting elements in list by using fold
Asked 2022-Jan-20 at 10:53I've got the task to write a function using fold (functional programming) to count the number of elements in a list that fulfill the predicate. I was given the function signature fun <A> count(list<A>, predicate: (A) -> Boolean): Int
. Fold shall not only be used as iteration, but also generate the return value. So I tried to do this:
1fun <A> count(list: List<A>, predicate: (A) -> Boolean): Int {
2 return list.fold(0) {
3 acc, a ->
4 if (predicate(a)) {
5 return acc + 1
6 }
7
8 return acc
9 }
10 }
11
I wrote a println to check if it works:
1fun <A> count(list: List<A>, predicate: (A) -> Boolean): Int {
2 return list.fold(0) {
3 acc, a ->
4 if (predicate(a)) {
5 return acc + 1
6 }
7
8 return acc
9 }
10 }
11println(count3(listOf (1, -2, 3, 10, -5, 8, 12), { it > 0 && it < 10 }))
12
However, I got the result 1 instead of 3 on the console and I don't know where the fault is. So, does anyone have an idea where my error is or how I can implement the function instead?
And just to be clear: Fold accumulates a value, starting with the initial value (in this case 0) and applying the operation from left to right to the current accumulator and each element or am I mistaken?
EDIT (I hope it's okay to edit a question instead of asking a new one):
Is it possible to return a whole list instead of just an int? I just found examples returning integers or booleans. What I tried: I've used the same function signature from above. But instead of return an Int, I want to return a list:
1fun <A> count(list: List<A>, predicate: (A) -> Boolean): Int {
2 return list.fold(0) {
3 acc, a ->
4 if (predicate(a)) {
5 return acc + 1
6 }
7
8 return acc
9 }
10 }
11println(count3(listOf (1, -2, 3, 10, -5, 8, 12), { it > 0 && it < 10 }))
12fun <A> returnList(list: List<A>, predicate: (A) -> Boolean): List<A> {
13 return list.fold(mutableListOf()) {
14 acc, a ->
15 if (predicate(a)) {
16 acc.add(a)
17 } else {
18 acc
19 }
20 }
21 }
22
The problem that I found is that acc.add(a)
returns a boolean and not a list, so that the IDE marks it as a mistake. So is there a way to return a list?
Thanks in Advance.
ANSWER
Answered 2022-Jan-19 at 17:06Try it like this:
1fun <A> count(list: List<A>, predicate: (A) -> Boolean): Int {
2 return list.fold(0) {
3 acc, a ->
4 if (predicate(a)) {
5 return acc + 1
6 }
7
8 return acc
9 }
10 }
11println(count3(listOf (1, -2, 3, 10, -5, 8, 12), { it > 0 && it < 10 }))
12fun <A> returnList(list: List<A>, predicate: (A) -> Boolean): List<A> {
13 return list.fold(mutableListOf()) {
14 acc, a ->
15 if (predicate(a)) {
16 acc.add(a)
17 } else {
18 acc
19 }
20 }
21 }
22fun <A> count(list: List<A>, predicate: (A) -> Boolean): Int {
23 return list.fold(0) { acc, a -> if (predicate(a)) acc+1 else acc }
24}
25
26fun main(args: Array<String>) {
27 val x = listOf<Int>( 1, -2, 3, 10, -5, 8, 12);
28 println(count(x, { it > 0 && it < 10 }))
29}
30
Looking at this site made the necessary change clear to me.
Is that form necessary because fold uses tail recursion? Interesting to see what the reason is.
QUESTION
Are java streams able to lazilly reduce from map/filter conditions?
Asked 2022-Jan-12 at 09:30I am using a functional programming style to solve the Leetcode easy question, Count the Number of Consistent Strings. The premise of this question is simple: count the amount of values for which the predicate of "all values are in another set" holds.
I have two approaches, one which I am fairly certain behaves as I want it to, and the other which I am less sure about. Both produce the correct output, but ideally they would stop evaluating other elements after the output is in a final state.
1 public int countConsistentStrings(String allowed, String[] words) {
2 final Set<Character> set = allowed.chars()
3 .mapToObj(c -> (char)c)
4 .collect(Collectors.toCollection(HashSet::new));
5 return (int)Arrays.stream(words)
6 .filter(word ->
7 word.chars()
8 .allMatch(c -> set.contains((char)c))
9 )
10 .count();
11 }
12
In this solution, to the best of my knowledge, the allMatch statement will terminate and evaluate to false at the first instance of c for which the predicate does not hold true, skipping the other values in that stream.
1 public int countConsistentStrings(String allowed, String[] words) {
2 final Set<Character> set = allowed.chars()
3 .mapToObj(c -> (char)c)
4 .collect(Collectors.toCollection(HashSet::new));
5 return (int)Arrays.stream(words)
6 .filter(word ->
7 word.chars()
8 .allMatch(c -> set.contains((char)c))
9 )
10 .count();
11 }
12 public int countConsistentStrings(String allowed, String[] words) {
13 Set<Character> set = allowed.chars()
14 .mapToObj(c -> (char)c)
15 .collect(Collectors.toCollection(HashSet::new));
16 return (int)Arrays.stream(words)
17 .filter(word ->
18 word.chars()
19 .mapToObj(c -> set.contains((char)c))
20 .reduce((a,b) -> a&&b)
21 .orElse(false)
22 )
23 .count();
24 }
25
In this solution, the same logic is used but instead of allMatch
, I use map
and then reduce
. Logically, after a single false
value comes from the map
stage, reduce
will always evaluate to false
. I know Java streams are lazy, but I am unsure when they ''know'' just how lazy they can be. Will this be less efficient than using allMatch
or will laziness ensure the same operation?
Lastly, in this code, we can see that the value for x
will always be 0 as after filtering for only positive numbers, the sum of them will always be positive (assume no overflow) so taking the minimum of positive numbers and a hardcoded 0 will be 0. Will the stream be lazy enough to evaluate this to 0 always, or will it work to reduce every element after the filter anyways?
1 public int countConsistentStrings(String allowed, String[] words) {
2 final Set<Character> set = allowed.chars()
3 .mapToObj(c -> (char)c)
4 .collect(Collectors.toCollection(HashSet::new));
5 return (int)Arrays.stream(words)
6 .filter(word ->
7 word.chars()
8 .allMatch(c -> set.contains((char)c))
9 )
10 .count();
11 }
12 public int countConsistentStrings(String allowed, String[] words) {
13 Set<Character> set = allowed.chars()
14 .mapToObj(c -> (char)c)
15 .collect(Collectors.toCollection(HashSet::new));
16 return (int)Arrays.stream(words)
17 .filter(word ->
18 word.chars()
19 .mapToObj(c -> set.contains((char)c))
20 .reduce((a,b) -> a&&b)
21 .orElse(false)
22 )
23 .count();
24 }
25List<Integer> list = new ArrayList<>();
26...
27/*Some values added to list*/
28...
29int x = list.stream()
30 .filter(i -> i >= 0)
31 .reduce((a,b) -> Math.min(a+b, 0))
32 .orElse(0);
33
To summarize the above, how does one know when the Java stream will be lazy? There are lazy opportunities that I see in the code, but how can I guarantee that my code will be as lazy as possible?
ANSWER
Answered 2022-Jan-12 at 09:30The actual term you’re asking for is short-circuiting
Further, some operations are deemed short-circuiting operations. An intermediate operation is short-circuiting if, when presented with infinite input, it may produce a finite stream as a result. A terminal operation is short-circuiting if, when presented with infinite input, it may terminate in finite time. Having a short-circuiting operation in the pipeline is a necessary, but not sufficient, condition for the processing of an infinite stream to terminate normally in finite time.
The term “lazy” only applies to intermediate operations and means that they only perform work when being requested by the terminal operation. This is always the case, so when you don’t chain a terminal operation, no intermediate operation will ever process any element.
Finding out whether a terminal operation is short-circuiting, is rather easy. Go to the Stream
API documentation and check whether the particular terminal operation’s documentation contains the sentence
This is a short-circuiting terminal operation.
allMatch
has it, reduce
has not.
This does not mean that such optimizations based on logic or algebra are impossible. But the responsibility lies at the JVM’s optimizer which might do the same for loops. However, this requires inlining of all involved methods to be sure that this conditions always applies and there are no side effect which must be retained. This behavioral compatibility implies that even if the processing gets optimized away, a peek(System.out::println)
would keep printing all elements as if they were processed. In practice, you should not expect such optimizations, as the Stream implementation code is too complex for the optimizer.
QUESTION
Two recursive functions and stackoverflow errors in javascript/nodeJs. Understanding the differences
Asked 2022-Jan-04 at 16:54Looking into the SICP book and JS functional programming I created two recursive functions. My expectation was that both of them raised a stack overflow error. But it is only the sumAll() function that raised the error. See below the code for both functions sumAll() and factorial():
As expected the sumAll() function did raise a stack overflow error
1function sumAll(n, i = 0, result = 0) {
2 return (i > n)
3 ? result
4 : sumAll(n, i + 1, i + result);
5}
6
7console.log(sumAll(10000));
The factorial() function below did not raise a stack overflow error:
1function sumAll(n, i = 0, result = 0) {
2 return (i > n)
3 ? result
4 : sumAll(n, i + 1, i + result);
5}
6
7console.log(sumAll(10000));function factorial(n){
8 return (n == 1)
9 ? 1
10 : n* factorial((n-1))
11}
12
13console.log(factorial(10000))
My question is why the factorial() function does not raise a stack overflow and works perfectly in nodeJS meanwhile the sumAll() did raise it also in nodeJS
ANSWER
Answered 2022-Jan-03 at 14:42I gave the below answer in error, I got mixed up about which function was throwing the exception. Please ignore.
Your first function is capable of taking advantage of tail-call optimization, while your second function is not (or it is, but perhaps not in a way that's implemented in the node.js language).
Consider this: your first function's usual condition is that it ends in return sumAll(n, i + 1, i + result)
, which means that once you get something to return, you can just return that.
Your second function however ends in return n* factorial((n-1))
, which means that once you get something to return, you have to do ANOTHER operation on it (multiply it by n) before you can actually return the result.
I believe the node.js interpreter isn't able to tail-call-optimize the second function because it requires another operation to be performed on it before the return.
Please note: I am not certain this is the answer, and I suspect node.js may not support tail call optimizations of any sort. This is my theory though about why one function might error in that way and the other one wouldn't.
QUESTION
Using '|' (pipe) operator with std::views does not compile
Asked 2021-Dec-02 at 11:55After a career diversion, I am trying to get up to speed with std::views (and functional programming in general). I am using the '|' (pipe) operator with std::views::filter on a vector, and I am puzzled why some code structures compile and others don't.
This code creates a vector of vectors of int
, then filters them by sum. I've commented the three statements that are confusing me, the first two of which compile and the third doesn't.
Compilation error is:
1'|': no operator found which takes a left-hand operand of type 'std::vector<std::vector<int,std::allocator<int>>,std::allocator<std::vector<int,std::allocator<int>>>>' (or there is no acceptable conversion)
2
(Using MSVC19, compiled with /std:c++latest
)
I am puzzled as to why this doesn't compile while (2) especially does?
1'|': no operator found which takes a left-hand operand of type 'std::vector<std::vector<int,std::allocator<int>>,std::allocator<std::vector<int,std::allocator<int>>>>' (or there is no acceptable conversion)
2#include <vector>
3#include <numeric>
4#include <ranges>
5
6template<typename T>
7auto buildMultiples(const std::vector<T>& base)
8{
9 std::vector<std::vector<T>> vRet;
10 for(T n= 1; n <= 5; n++)
11 {
12 auto v = base;
13 for (auto& m : v) m *= n;
14 vRet.push_back(v);
15 }
16 return vRet;
17}
18
19template<typename T>
20struct sumGreaterThan
21{
22 T _limit{ 0 };
23
24 auto operator()(const std::vector<T>& v) {return std::accumulate(v.cbegin(), v.cend(), 0) > _limit;}
25};
26
27int main()
28{
29 using namespace std;
30 vector<int> nums{1,2,3,4,5,6,7,8,9};
31
32 auto mults = buildMultiples(nums);
33
34 for (auto& m : buildMultiples(nums)) {} //1. Compiles
35
36 sumGreaterThan sumFilter{ 10 };
37
38 auto vecs = buildMultiples(nums);
39 for (auto& m : vecs | views::filter(sumFilter)) {} //2. Compiles
40
41 for (auto& m : buildMultiples(nums) | views::filter(sumFilter)) {} //3. Compilation Error!!
42
43 for (auto vecs = buildMultiples(nums); auto & m : vecs | views::filter(sumFilter)) {} // 4. Compiles. Thanks @Aryter
44}
45
ANSWER
Answered 2021-Dec-01 at 22:04This is passing an lvalue vector
into filter
:
1'|': no operator found which takes a left-hand operand of type 'std::vector<std::vector<int,std::allocator<int>>,std::allocator<std::vector<int,std::allocator<int>>>>' (or there is no acceptable conversion)
2#include <vector>
3#include <numeric>
4#include <ranges>
5
6template<typename T>
7auto buildMultiples(const std::vector<T>& base)
8{
9 std::vector<std::vector<T>> vRet;
10 for(T n= 1; n <= 5; n++)
11 {
12 auto v = base;
13 for (auto& m : v) m *= n;
14 vRet.push_back(v);
15 }
16 return vRet;
17}
18
19template<typename T>
20struct sumGreaterThan
21{
22 T _limit{ 0 };
23
24 auto operator()(const std::vector<T>& v) {return std::accumulate(v.cbegin(), v.cend(), 0) > _limit;}
25};
26
27int main()
28{
29 using namespace std;
30 vector<int> nums{1,2,3,4,5,6,7,8,9};
31
32 auto mults = buildMultiples(nums);
33
34 for (auto& m : buildMultiples(nums)) {} //1. Compiles
35
36 sumGreaterThan sumFilter{ 10 };
37
38 auto vecs = buildMultiples(nums);
39 for (auto& m : vecs | views::filter(sumFilter)) {} //2. Compiles
40
41 for (auto& m : buildMultiples(nums) | views::filter(sumFilter)) {} //3. Compilation Error!!
42
43 for (auto vecs = buildMultiples(nums); auto & m : vecs | views::filter(sumFilter)) {} // 4. Compiles. Thanks @Aryter
44}
45vecs | views::filter(sumFilter)
46
whereas this is passing an rvalue vector
into filter
:
1'|': no operator found which takes a left-hand operand of type 'std::vector<std::vector<int,std::allocator<int>>,std::allocator<std::vector<int,std::allocator<int>>>>' (or there is no acceptable conversion)
2#include <vector>
3#include <numeric>
4#include <ranges>
5
6template<typename T>
7auto buildMultiples(const std::vector<T>& base)
8{
9 std::vector<std::vector<T>> vRet;
10 for(T n= 1; n <= 5; n++)
11 {
12 auto v = base;
13 for (auto& m : v) m *= n;
14 vRet.push_back(v);
15 }
16 return vRet;
17}
18
19template<typename T>
20struct sumGreaterThan
21{
22 T _limit{ 0 };
23
24 auto operator()(const std::vector<T>& v) {return std::accumulate(v.cbegin(), v.cend(), 0) > _limit;}
25};
26
27int main()
28{
29 using namespace std;
30 vector<int> nums{1,2,3,4,5,6,7,8,9};
31
32 auto mults = buildMultiples(nums);
33
34 for (auto& m : buildMultiples(nums)) {} //1. Compiles
35
36 sumGreaterThan sumFilter{ 10 };
37
38 auto vecs = buildMultiples(nums);
39 for (auto& m : vecs | views::filter(sumFilter)) {} //2. Compiles
40
41 for (auto& m : buildMultiples(nums) | views::filter(sumFilter)) {} //3. Compilation Error!!
42
43 for (auto vecs = buildMultiples(nums); auto & m : vecs | views::filter(sumFilter)) {} // 4. Compiles. Thanks @Aryter
44}
45vecs | views::filter(sumFilter)
46buildMultiples(nums) | views::filter(sumFilter)
47
The current rule, which compilers implement, is that range adaptor pipelines cannot take rvalue non-view ranges (like vector
, string
, etc.). This is because the pipeline itself is non-owning (views were non-owning), and exists as a safety mechanism to prevent dangling.
The new rule, recently adopted as a defect, would allow this could and would cause filter
to own the result of buildMultiples
(this is P2415), but compilers don't implement it quite yet. With this change, your other version would also have compiled.
So for now, you will have to keep writing it this way (as you are already doing):
1'|': no operator found which takes a left-hand operand of type 'std::vector<std::vector<int,std::allocator<int>>,std::allocator<std::vector<int,std::allocator<int>>>>' (or there is no acceptable conversion)
2#include <vector>
3#include <numeric>
4#include <ranges>
5
6template<typename T>
7auto buildMultiples(const std::vector<T>& base)
8{
9 std::vector<std::vector<T>> vRet;
10 for(T n= 1; n <= 5; n++)
11 {
12 auto v = base;
13 for (auto& m : v) m *= n;
14 vRet.push_back(v);
15 }
16 return vRet;
17}
18
19template<typename T>
20struct sumGreaterThan
21{
22 T _limit{ 0 };
23
24 auto operator()(const std::vector<T>& v) {return std::accumulate(v.cbegin(), v.cend(), 0) > _limit;}
25};
26
27int main()
28{
29 using namespace std;
30 vector<int> nums{1,2,3,4,5,6,7,8,9};
31
32 auto mults = buildMultiples(nums);
33
34 for (auto& m : buildMultiples(nums)) {} //1. Compiles
35
36 sumGreaterThan sumFilter{ 10 };
37
38 auto vecs = buildMultiples(nums);
39 for (auto& m : vecs | views::filter(sumFilter)) {} //2. Compiles
40
41 for (auto& m : buildMultiples(nums) | views::filter(sumFilter)) {} //3. Compilation Error!!
42
43 for (auto vecs = buildMultiples(nums); auto & m : vecs | views::filter(sumFilter)) {} // 4. Compiles. Thanks @Aryter
44}
45vecs | views::filter(sumFilter)
46buildMultiples(nums) | views::filter(sumFilter)
47auto vecs = buildMultiples(nums);
48for (auto& m : vecs | views::filter(sumFilter)) { ... }
49
QUESTION
Apply a filter on a multilevel object based on a different object
Asked 2021-Nov-25 at 21:30I have an array with multilevel objects, such as:
1list= [ {
2 type: { name: 'dog', size:'medium'},
3 entity: { name: 'Tobby', age: '7'},
4 },
5 {
6 type: { name: 'cat', size:'small'},
7 entity: { name: 'Garfield', age: '7'},
8 }
9 ]
10
And I have a defined filter such as:
1list= [ {
2 type: { name: 'dog', size:'medium'},
3 entity: { name: 'Tobby', age: '7'},
4 },
5 {
6 type: { name: 'cat', size:'small'},
7 entity: { name: 'Garfield', age: '7'},
8 }
9 ]
10filter={
11 type: { name:'dog'}}
12
I need to define a function such that I can call
1list= [ {
2 type: { name: 'dog', size:'medium'},
3 entity: { name: 'Tobby', age: '7'},
4 },
5 {
6 type: { name: 'cat', size:'small'},
7 entity: { name: 'Garfield', age: '7'},
8 }
9 ]
10filter={
11 type: { name:'dog'}}
12let filtered = list.thisMagicFilter(filter)
13
that will filter accordingly on my list (in the given example, returning only the first element.
Logically, if the filter is {entity:{age:'7'}}
, then both results should be returned.
I've broken my head trying to do this in a functional programming with a list.filter()
, but it's just too complex for me: I'm struggling on the fact that it's a 2 level validation.
Can I get some hint of how to tackle this problem?
Thank you in advance!
ANSWER
Answered 2021-Nov-25 at 21:25Try this:
1list= [ {
2 type: { name: 'dog', size:'medium'},
3 entity: { name: 'Tobby', age: '7'},
4 },
5 {
6 type: { name: 'cat', size:'small'},
7 entity: { name: 'Garfield', age: '7'},
8 }
9 ]
10filter={
11 type: { name:'dog'}}
12let filtered = list.thisMagicFilter(filter)
13const _matchesFilter = (e, conditions = []) =>
14 conditions.every(([prop, condition]) => {
15 const subConditions = Object.entries(condition);
16 return subConditions.every(([ subProp, subCondition ]) =>
17 e[prop]?.[subProp] === subCondition
18 );
19 });
20
21const thisMagicFilter = (list = [], filter = {}) => {
22 const conditions = Object.entries(filter);
23 return list.filter(e => _matchesFilter(e, conditions));
24}
25
26const list= [
27 { type: { name: 'dog', size:'medium'}, entity: { name: 'Tobby', age: '7'} },
28 { type: { name: 'cat', size:'small'}, entity: { name: 'Garfield', age: '7'} }
29];
30console.log( thisMagicFilter(list) );
31console.log( thisMagicFilter(list, { type: { name: 'dog'} }) );
32console.log( thisMagicFilter(list, { entity: { age: '7'} }) );
33console.log( thisMagicFilter(list, { entity: { name: 'Garfield', age: '7'} }) );
34console.log( thisMagicFilter(list, { type: { size: 'medium' }, entity: { age: '7'} }) );
QUESTION
What is the relationship between monad functions dist and join in Haskell?
Asked 2021-Nov-22 at 12:41I was doing one of the homeworks from functional programming course and found some problems understanding monads in Haskell.
So, we were given a type:
1data Annotated e a = a :# e
2infix 0 :#
3
The task was to implement some functions with given type signatures, which I did. They pass needed tests (separately):
1data Annotated e a = a :# e
2infix 0 :#
3mapAnnotated :: (a -> b) -> (Annotated e a -> Annotated e b)
4mapAnnotated f (x :# w) = f x :# w
5
6joinAnnotated :: Semigroup e => Annotated e (Annotated e a) -> Annotated e a
7joinAnnotated ((b :# m) :# n) = b :# m <> n
8
9distAnnotated :: Semigroup e => (Annotated e a, Annotated e b) -> Annotated e (a, b)
10distAnnotated (x :# m, y :# n) = (x, y) :# m <> n
11
However, we were also asked to satisfy following equation:
1data Annotated e a = a :# e
2infix 0 :#
3mapAnnotated :: (a -> b) -> (Annotated e a -> Annotated e b)
4mapAnnotated f (x :# w) = f x :# w
5
6joinAnnotated :: Semigroup e => Annotated e (Annotated e a) -> Annotated e a
7joinAnnotated ((b :# m) :# n) = b :# m <> n
8
9distAnnotated :: Semigroup e => (Annotated e a, Annotated e b) -> Annotated e (a, b)
10distAnnotated (x :# m, y :# n) = (x, y) :# m <> n
11distAnnotated (p, q) = joinAnnotated (mapAnnotated (\a -> mapAnnotated (\b -> (a, b)) q) p)
12
I can't quite get my head around so many function applications, so for other types with similar tasks I just did what seemed "natural" and it worked, but here it doesn't and I can't see why, since I don't even see other ways to implement these functions. What am I missing?
ANSWER
Answered 2021-Nov-21 at 19:19Let's start with the troublesome equation and systematically substitute the definitions, working it inside-out:
1data Annotated e a = a :# e
2infix 0 :#
3mapAnnotated :: (a -> b) -> (Annotated e a -> Annotated e b)
4mapAnnotated f (x :# w) = f x :# w
5
6joinAnnotated :: Semigroup e => Annotated e (Annotated e a) -> Annotated e a
7joinAnnotated ((b :# m) :# n) = b :# m <> n
8
9distAnnotated :: Semigroup e => (Annotated e a, Annotated e b) -> Annotated e (a, b)
10distAnnotated (x :# m, y :# n) = (x, y) :# m <> n
11distAnnotated (p, q) = joinAnnotated (mapAnnotated (\a -> mapAnnotated (\b -> (a, b)) q) p)
12-- Given
13mapAnnotated f (x :# w) = f x :# w
14joinAnnotated ((b :# m) :# n) = b :# m <> n
15distAnnotated (x :# m, y :# n) = (x, y) :# m <> n
16p = x :# m
17q = y :# n
18
19-- Goal
20distAnnotated (p, q) = joinAnnotated (mapAnnotated (\a -> mapAnnotated (\b -> (a, b)) q) p)
21
22-- Right-hand side
23joinAnnotated (mapAnnotated (\a -> mapAnnotated (\b -> (a, b)) q) p)
24joinAnnotated (mapAnnotated (\a -> mapAnnotated (\b -> (a, b)) (y :# n)) (x :# m))
25joinAnnotated (mapAnnotated (\a -> (\b -> (a, b)) y :# n) (x :# m))
26joinAnnotated (mapAnnotated (\a -> (a, y) :# n) (x :# m))
27joinAnnotated (mapAnnotated (\a -> (a, y) :# n) (x :# m))
28joinAnnotated ((\a -> (a, y) :# n) x :# m)
29joinAnnotated (((x, y) :# n) :# m)
30(x, y) :# n <> m
31-- Left-hand side
32distAnnotated (p, q)
33distAnnotated (x :# m, y :# n)
34(x, y) :# m <> n
35-- LHS /= RHS
36
The problem, therefore, is that distAnnotated
combines the annotations in a different order than joinAnnotated
(m <> n
versus n <> m
). The usual way to make them agree is changing joinAnnotated
so that the outside annotation comes first:
1data Annotated e a = a :# e
2infix 0 :#
3mapAnnotated :: (a -> b) -> (Annotated e a -> Annotated e b)
4mapAnnotated f (x :# w) = f x :# w
5
6joinAnnotated :: Semigroup e => Annotated e (Annotated e a) -> Annotated e a
7joinAnnotated ((b :# m) :# n) = b :# m <> n
8
9distAnnotated :: Semigroup e => (Annotated e a, Annotated e b) -> Annotated e (a, b)
10distAnnotated (x :# m, y :# n) = (x, y) :# m <> n
11distAnnotated (p, q) = joinAnnotated (mapAnnotated (\a -> mapAnnotated (\b -> (a, b)) q) p)
12-- Given
13mapAnnotated f (x :# w) = f x :# w
14joinAnnotated ((b :# m) :# n) = b :# m <> n
15distAnnotated (x :# m, y :# n) = (x, y) :# m <> n
16p = x :# m
17q = y :# n
18
19-- Goal
20distAnnotated (p, q) = joinAnnotated (mapAnnotated (\a -> mapAnnotated (\b -> (a, b)) q) p)
21
22-- Right-hand side
23joinAnnotated (mapAnnotated (\a -> mapAnnotated (\b -> (a, b)) q) p)
24joinAnnotated (mapAnnotated (\a -> mapAnnotated (\b -> (a, b)) (y :# n)) (x :# m))
25joinAnnotated (mapAnnotated (\a -> (\b -> (a, b)) y :# n) (x :# m))
26joinAnnotated (mapAnnotated (\a -> (a, y) :# n) (x :# m))
27joinAnnotated (mapAnnotated (\a -> (a, y) :# n) (x :# m))
28joinAnnotated ((\a -> (a, y) :# n) x :# m)
29joinAnnotated (((x, y) :# n) :# m)
30(x, y) :# n <> m
31-- Left-hand side
32distAnnotated (p, q)
33distAnnotated (x :# m, y :# n)
34(x, y) :# m <> n
35-- LHS /= RHS
36joinAnnotated ((b :# m) :# n) = b :# n <> m
37
This fits both the natural sequencing of computations in the monadic bind (m >>= f = joinAnnotated (mapAnnotated f m)
) and the conventional left-to-right order of applicative effects (p <*> q = ap p q = mapAnnotated (\(f, a) -> f a) (distAnnotated (p, q))
).
QUESTION
How to Log in F# in a functional way using NLog
Asked 2021-Nov-21 at 15:55I have been looking at logging options and settled on NLog and logging to a database
I am fairly new to functional programming and have come from a background in C# OOP.
How would I implement logging in a functional way in F#?
Do I
- Create the logger at the top level and just pass it in to every function as I go
- Access the logger through a static method as needed ( obviously there would be some overhead to instantiating a logger each time - but maybe that's not a big deal )
- Something else?
I want to avoid using a commercial logging option just because my projects are quite small.
Thanks for your time.
ANSWER
Answered 2021-Nov-20 at 00:43As logging is inherently impure there isn't a particularly clean way to do logging that I'm aware of. You have basically identified the two solutions in your question. Which one you use depends on what the logs are being used for.
For logging to external services I would consider creating an AppContext
type which is home to app and user settings as well as providing functions or methods for logging to e.g. a database. This type should be added an extra parameter in your functions or an additional field in your types depending on what makes the most sense.
For your lowest-level functions rather than changing them all to accept an additional parameter you should consider altering the return type to include the information you want to log and leaving the act of logging to higher level parts of your program.
For logging to a console, rolling buffer, or other temporary location I think it is fine to create a module
which is equivalent to a C# static class and just provide globally accessible logging functions.
QUESTION
Idiomatically moving/sorting values from one Vec into another
Asked 2021-Sep-22 at 22:48I recently picked up rust, coming from a python background. I'm still getting the hang of functional programming so I'm looking for insight/feedback on writing idiomatic rust.
In the example below I have a list of Parent
elements and Child
elements and want to sort the Child
elements into their respective parents based off of an id
.
In python I would nest two for loops, perform a test and continue accordingly. But I'm not quite sure if there is a better/performant/idiomatic way of doing this.
I've marked the section of the code in question. Although any feedback is great!
Here's a working playgound: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=233cfa5b5798090fa969ba348a479b1c
1#[derive(Debug)]
2struct Parent {
3 id: String,
4 children: Vec<Child>,
5}
6
7impl Parent {
8 pub fn from_id(id: String) -> Self {
9 Self {
10 id,
11 children: Vec::new(),
12 }
13 }
14}
15
16#[derive(Debug)]
17struct Child {
18 parent_id: String,
19}
20
21impl Child {
22 pub fn from_parent_id(parent_id: String) -> Self {
23 Self { parent_id }
24 }
25}
26
27fn main() {
28 let mut parents: Vec<Parent> = vec!["a", "b", "c"]
29 .iter()
30 .map(|s| s.to_string())
31 .map(Parent::from_id)
32 .collect();
33
34 let mut children: Vec<Child> = vec!["a", "a", "b", "c", "c", "c"]
35 .iter()
36 .map(|s| s.to_string())
37 .map(Child::from_parent_id)
38 .collect();
39
40 // Is there a better way to do this?
41 while let Some(child) = children.pop() {
42 for parent in parents.iter_mut() {
43 if child.parent_id == parent.id {
44 parent.children.push(child);
45 break;
46 }
47 }
48 }
49
50 dbg!(parents);
51 dbg!(children);
52}
53
ANSWER
Answered 2021-Sep-22 at 06:49Popping items off the end of the vector is mostly used when you need to retain parts or all of the vector. If you need to consume the whole vector, you can pass it to the for
loop directly:
1#[derive(Debug)]
2struct Parent {
3 id: String,
4 children: Vec<Child>,
5}
6
7impl Parent {
8 pub fn from_id(id: String) -> Self {
9 Self {
10 id,
11 children: Vec::new(),
12 }
13 }
14}
15
16#[derive(Debug)]
17struct Child {
18 parent_id: String,
19}
20
21impl Child {
22 pub fn from_parent_id(parent_id: String) -> Self {
23 Self { parent_id }
24 }
25}
26
27fn main() {
28 let mut parents: Vec<Parent> = vec!["a", "b", "c"]
29 .iter()
30 .map(|s| s.to_string())
31 .map(Parent::from_id)
32 .collect();
33
34 let mut children: Vec<Child> = vec!["a", "a", "b", "c", "c", "c"]
35 .iter()
36 .map(|s| s.to_string())
37 .map(Child::from_parent_id)
38 .collect();
39
40 // Is there a better way to do this?
41 while let Some(child) = children.pop() {
42 for parent in parents.iter_mut() {
43 if child.parent_id == parent.id {
44 parent.children.push(child);
45 break;
46 }
47 }
48 }
49
50 dbg!(parents);
51 dbg!(children);
52}
53for child in children {
54 for parent in parents.iter_mut() {
55 if child.parent_id == parent.id {
56 parent.children.push(child);
57 break;
58 }
59 }
60}
61
You can use iterators to look for the parent, like this:
1#[derive(Debug)]
2struct Parent {
3 id: String,
4 children: Vec<Child>,
5}
6
7impl Parent {
8 pub fn from_id(id: String) -> Self {
9 Self {
10 id,
11 children: Vec::new(),
12 }
13 }
14}
15
16#[derive(Debug)]
17struct Child {
18 parent_id: String,
19}
20
21impl Child {
22 pub fn from_parent_id(parent_id: String) -> Self {
23 Self { parent_id }
24 }
25}
26
27fn main() {
28 let mut parents: Vec<Parent> = vec!["a", "b", "c"]
29 .iter()
30 .map(|s| s.to_string())
31 .map(Parent::from_id)
32 .collect();
33
34 let mut children: Vec<Child> = vec!["a", "a", "b", "c", "c", "c"]
35 .iter()
36 .map(|s| s.to_string())
37 .map(Child::from_parent_id)
38 .collect();
39
40 // Is there a better way to do this?
41 while let Some(child) = children.pop() {
42 for parent in parents.iter_mut() {
43 if child.parent_id == parent.id {
44 parent.children.push(child);
45 break;
46 }
47 }
48 }
49
50 dbg!(parents);
51 dbg!(children);
52}
53for child in children {
54 for parent in parents.iter_mut() {
55 if child.parent_id == parent.id {
56 parent.children.push(child);
57 break;
58 }
59 }
60}
61for child in children {
62 parents
63 .iter_mut()
64 .find(|parent| parent.id == child.parent_id)
65 .map(|parent| parent.children.push(child));
66}
67
The most important issue with performance is that this needs to perform n*m
iterations in total, where n
and m
are number of parents and children. If those numbers can go into tens of thousands, you will end up with hundreds of millions of iterations, which will slow you down. You can create a temporary mapping of id->position for the parents vector can make the operation O(n + m)
:
1#[derive(Debug)]
2struct Parent {
3 id: String,
4 children: Vec<Child>,
5}
6
7impl Parent {
8 pub fn from_id(id: String) -> Self {
9 Self {
10 id,
11 children: Vec::new(),
12 }
13 }
14}
15
16#[derive(Debug)]
17struct Child {
18 parent_id: String,
19}
20
21impl Child {
22 pub fn from_parent_id(parent_id: String) -> Self {
23 Self { parent_id }
24 }
25}
26
27fn main() {
28 let mut parents: Vec<Parent> = vec!["a", "b", "c"]
29 .iter()
30 .map(|s| s.to_string())
31 .map(Parent::from_id)
32 .collect();
33
34 let mut children: Vec<Child> = vec!["a", "a", "b", "c", "c", "c"]
35 .iter()
36 .map(|s| s.to_string())
37 .map(Child::from_parent_id)
38 .collect();
39
40 // Is there a better way to do this?
41 while let Some(child) = children.pop() {
42 for parent in parents.iter_mut() {
43 if child.parent_id == parent.id {
44 parent.children.push(child);
45 break;
46 }
47 }
48 }
49
50 dbg!(parents);
51 dbg!(children);
52}
53for child in children {
54 for parent in parents.iter_mut() {
55 if child.parent_id == parent.id {
56 parent.children.push(child);
57 break;
58 }
59 }
60}
61for child in children {
62 parents
63 .iter_mut()
64 .find(|parent| parent.id == child.parent_id)
65 .map(|parent| parent.children.push(child));
66}
67let parent_pos_by_id: HashMap<_, _> = parents
68 .iter()
69 .enumerate()
70 .map(|(idx, parent)| (parent.id.clone(), idx))
71 .collect();
72
73for child in children {
74 if let Some(&parent_pos) = parent_pos_by_id.get(&child.parent_id) {
75 parents[parent_pos].children.push(child);
76 }
77}
78
Community Discussions contain sources that include Stack Exchange Network
Tutorials and Learning Resources in Functional Programming
Tutorials and Learning Resources are not available at this moment for Functional Programming