R.evolve lets us replace object properties with the result of a function applied to that property's current value:
R.evolve({ count: R.inc }, { count: 1 })
== { count: 2 }
But I frequently find I want to add a property calculated from multiple properties of input object:
assocFruitTotal({ appleCount: 5, orangeCount: 3 })
== { appleCount: 5, orangeCount: 3, fruitCount: 8 }
I came up with my own simple utility function:
const assocDerived = R.curry(
(name, f, obj) => ({
...obj,
[name]: f(obj)
});
... and I use it a lot:
const sumFruit = R.pipe(
R.props(['appleCount', 'orangeCount']),
R.sum);
const assocFruitTotal = assocDerived('fruitCount', sumFruit);
But the sheer frequency with which I use this makes me wonder why it's not
native to Ramda, as so many other convenient functions are. And that makes
me wonder whether I'm missing a better idiom that achieves the outcome -- that is, building up detail in an object by adding properties based upon combinations of other properties.
Is there an idiomatic functional programming construct I should be using instead?
Personally I would do it this way:
const fruitCount = applySpec({fruitCount: compose(sum, values)})
fruitCount({apple: 5, orange: 3})
//=> {"fruitCount": 8}
const withFruitCount = converge(mergeRight, [identity, fruitCount]);
withFruitCount({apple: 5, orange: 3});
//=> {"apple": 5, "fruitCount": 8, "orange": 3}
If there are non-count properties to exclude from the sum, you can use pickBy:
const pickCount = pickBy(flip(includes('Count')));
pickCount({appleCount: 5, orangeCount: 3, foo: 'bar'});
//=> {"appleCount": 5, "orangeCount": 3}
Let's start by recognizing that obj.value = f(obj) is a mutable assignment and therefore not a functional idiom to begin with. This is imperative-style thinking at work.
Storing a computed value as a property on your object is a misstep, in most cases. If either appleCount or orangeCount changes, there's nothing there to enforce the integrity of fruitCount.
fruitCount should be a function, not a property.
const fruitCount =
pipe
( props ([ 'appleCount', 'orangeCount' ])
, sum
)
fruitCount ({ appleCount: 1, orangeCount: 3 }) // 4
fruitCount ({ appleCount: 5, orangeCount: 3 }) // 8
If I had to guess, this is fake data and an example problem. In some scenarios, a computed value does make sense (memoisation is the first technique that comes to mind) but those cases make up the exception, not the rule. You say "the sheer frequency with which I use this ...", so I'd wager you do it in more areas than you should.
And as you pointed out, Ramda doesn't have a built-in for this, so this should further indicate that there are more conventional ways of solving this kind of problem.
An object-oriented programmer would assign this as a computed property -
const FruitData = function (apples = 0, oranges = 0)
{ this.apples = apples
this.oranges = oranges
}
Object.defineProperty
( FruitData.prototype
, 'fruitCount'
, { get () { return this.apples + this.oranges } }
)
const f =
new FruitData (3, 4)
console .log (f.fruitCount) // 7
When writing functional style, we leave OOP concepts at the door. Start thinking in terms of functions and your problems go away -
const FruitData = (apples = 0, oranges = 0) =>
({ apples, oranges })
const appleCount = fd =>
fd.apples
const orangeCount = fd =>
fd.oranges
const fruitCount = fd =>
appleCount (fd) + orangeCount (fd)
console .log (fruitCount (FruitData (10, 3))) // 13
Related
By using the Godot engine and writing in the GDScript language,
let's say I have an enum declared as:
enum eTextMode {CHAR, NUMBER, SYMBOLS_TEXT, SYMBOLS_ALL}
And an export variable as:
export(eTextMode, FLAGS) var _id: int = 0
In the inspector panel I can see which flag is selected or not, but how can I know in code which specifically flag is selected?
By selecting in the inspector, for example: the NUMBER and SYMBOLS_TEXT flags, the _id variable will be set as 5
My approach is the following hard-coded dictionary:
var _selected_flags: Dictionary = {
CHAR = _id in [1, 3, 5, 7, 9, 11, 13, 15],
NUMBER = _id in [2, 3, 6, 7, 10, 11, 14, 15],
SYMBOLS_TEXT = _id in [4, 5, 6, 7, 12, 13, 14, 15],
SYMBOLS_ALL = _id in [8, 9, 10, 11, 12, 13, 14, 15]
}
Resulting in:
{CHAR:True, NUMBER:False, SYMBOLS_ALL:False, SYMBOLS_TEXT:True}
The above result is exactly what I'm expecting (a dictionary with string keys as they are defined in the enum with a boolean value representing the selection state).
How could I manage to do this dynamically for any enum regardless of size?
Thank you very much,
One tacky solution that I could manage is by not using an enum at all, but instead a dictionary like the following example:
const dTextMode: Dictionary = {CHAR = false, NUMBER = false, SYMBOLS_TEXT = false, SYMBOLS_ALL = false}
export(Dictionary) var m_dTextMode: Dictionary = dTextMode setget Set_TextMode, Get_TextMode
func Get_TextMode() -> Dictionary: return m_dTextMode
func Set_TextMode(_data: Dictionary = m_dTextMode) -> void: m_dTextMode = _data
An exported dictionary is not as good-looking as an exported enum with FLAGS, and by following this approach it kind of invalidates my initial problem.
By selecting CHAR and SYMBOLS_TEXT in the exported dictionary from the inspector, and then calling print(self.Get_TextMode()) the result is indeed what I expected:
{CHAR:True, NUMBER:False, SYMBOLS_ALL:False, SYMBOLS_TEXT:True}
I still can't figure out though how to achieve this result by using the export(*enum, FLAGS) aproach.
Edit: also, the setter function is not feasible to be used in script since the user must know to duplicate the dTextMode constant first, edit it and set is as an argument.
Thanks to the comments from #Thearot from my first answer, I have managed to figure out the following solution which meets all expectations, with one caveat: it seems like an overkill solution...
enum eTestFlags {FLAG_1, FLAG_2, FLAG_3, FLAG_5, FLAG_6}
export(eTestFlags, FLAGS) var m_iTestFlags: int = 0 setget Set_TestFlags
func Get_TestFlags() -> Dictionary: return self._get_enum_flags(m_iTestFlags, eTestFlags)
func Set_TestFlags(_id: int = m_iTestFlags) -> void: m_iTestFlags = _id
func _get_enum_flags(_val_selected: int, _enum: Dictionary, _bit_check_limit: int = 32) -> Dictionary:
var _enum_keys: Array = _enum.keys() ; _enum_keys.invert()
var _bin_string: String = ""
var _val_temp: int = 0
var _val_count: int = _bit_check_limit - int(_is_pow2(_bit_check_limit))
while(_val_count >= 0):
_val_temp = _val_selected >> _val_count
_bin_string += "1" if _val_temp & 1 else "0"
_val_count -= 1
var _bin_string_padded: String = "%0*d" % [_enum_keys.size(), int(_bin_string)]
var _result_dict: Dictionary = {}
for _str_id in range(_bin_string_padded.length(), 0, -1):
_result_dict[_enum_keys[_str_id - 1]] = bool(_bin_string_padded[_str_id - 1] == "1")
return _result_dict
func _is_pow2(_value: int) -> bool:
return _value && (not (_value & (_value - 1)))
Now, if I print(self.Get_TestFlags()) after selecting FLAG_2 and FLAG_6 the result is:
{FLAG_1:False, FLAG_2:True, FLAG_3:False, FLAG_5:False, FLAG_6:True}
You're on the right track but overcomplicating things. Without going too much into the math (see Wikipedia), here's what you'd do in Godot:
enum eTextMode {CHAR, NUMBER, SYMBOLS_TEXT, SYMBOLS_ALL}
export(eTextMode, FLAGS) var _id: int = 0
func _ready() -> void:
for modeName in eTextMode:
var bit_flag_value: int = int(pow(2, eTextMode[modeName]))
if _id & bit_flag_value:
printt("Flagged", modeName)
You can access the named fields of your enum like elements in an Array/Dictionary by default (iterate through the keys, get their 0-based index as values). The above math trick turns the 0-based index into the correct bit flag number, and if you (single) '&' it with the combined bit-flags value you can check whether or not that flag is set.
I'm trying to sort all of my comments so that the replies will be threaded under their parent comment, going from oldest to newest in both the original comment along with their replies.
Comments will have an id, a date, and an optional parentId.
Parent Comments will not have a parentId, whereas the replies will have a parentId that is the id of their parent comment.
I'm getting my data from Firebase and adding it to a List.
From that List, I am separating out the comments that contain a parentId and those that do not.
final commentsWithoutParentID = comments
.where(
(c) => c.parentId.isEmpty,
).toList();
final commentsWithParentID = comments
.where(
(c) => c.parentId.isNotEmpty,
).toList();
I then sort these as:
// Sort comments with no parent id by date
commentsWithoutParentID.sort((a, b) => a.date!.compareTo(b.date!));
// Sort comments with a parentId by date
commentsWithParentID.sort((a, b) => a.date!.compareTo(b.date!));
// Sort comments with a parentId by comparing the various parentId
commentsWithParentID.sort((a, b) => a.parentId.compareTo(b.parentId));
After this I add these two Lists to an empty List:
sortedComments = commentsWithoutParentID + commentsWithParentID;
I then sort this to compare the parentId to the id:
sortedComments.sort((a, b) => a.parentId.compareTo(b.id));
The result I am getting is that all of the comments are sorted by date from oldest at the top to newest at the bottom and some of the replies get threaded under the incorrect parent comment.
So it seems that the date is taking a higher priority possibly.
I've altered these sorts but I either get the same result as I am getting now or something that is further from my desired result.
When you call sort after other sort, you break previous.
Consider group method (dart:collection dependency is required).
Or you can use temporary map to group children.
import 'package:collection/collection.dart';
class Comment {
final String date;
final int id;
Comment(this.date, this.id);
#override
String toString() => "($date, $id)";
}
class ThreadComment extends Comment {
final int parentId;
ThreadComment(String date, int id, this.parentId) : super(date, id);
#override
String toString() => "(parentId: $parentId, $date, $id)";
}
final threadComments = [
ThreadComment("day1", 1, 1),
ThreadComment("day2", 4, 1),
ThreadComment("day2", 3, 3),
ThreadComment("day1", 2, 3),
];
final parentComments = [
Comment("day3", 5),
Comment("day1", 1),
Comment("day2", 3),
Comment("day1", 2),
Comment("day3", 4),
];
int compare(v1, v2) => v1.date.compareTo(v2.date);
final comments = <Comment, List<ThreadComment>>{};
// group comments by parent id
for (final Comment c
in parentComments.sorted((a, b) => a.id.compareTo(b.id))) {
comments[c] = threadComments
.where((element) => element.parentId == c.id)
// sort children by date
.sorted(compare);
}
// sort parents by date
final sortedMap = comments.keys
.toList()
.sorted(compare)
.asMap()
.map((_, v) => MapEntry(v, comments[v]));
//expand map to one list
final sortedList = <List<Comment>>[
for (final parent in sortedMap.keys) [parent, ...?sortedMap[parent]]
].expand((element) => element).toList();
sortedList.forEach((element) => print("$element\n"));
// (day1, 1)
// (parentId: 1, day1, 1)
// (parentId: 1, day2, 4)
// (day1, 2)
// (day2, 3)
// (parentId: 3, day1, 2)
// (parentId: 3, day2, 3)
// (day3, 4)
// (day3, 5)
What is the efficient way to convert an array of pixelValues [UInt8] into two dimensional array of pixelValues rows - [[UInt8]]
You can write something like this:
var pixels: [UInt8] = [0,1,2,3, 4,5,6,7, 8,9,10,11, 12,13,14,15]
let bytesPerRow = 4
assert(pixels.count % bytesPerRow == 0)
let pixels2d: [[UInt8]] = stride(from: 0, to: pixels.count, by: bytesPerRow).map {
Array(pixels[$0..<$0+bytesPerRow])
}
But with the value semantics of Swift Arrays, all attempt to create new nested Array requires copying the content, so may not be "efficient" enough for your purpose.
Re-consider if you really need such nested Array.
This should work
private func convert1Dto2DArray(oneDArray:[String], stringsPerRow:Int)->[[String]]?{
var target = oneDArray
var outOfIndexArray:[String] = [String]()
let reminder = oneDArray.count % stringsPerRow
if reminder > 0 && reminder <= stringsPerRow{
let suffix = oneDArray.suffix(reminder)
let list = oneDArray.prefix(oneDArray.count - reminder)
target = Array(list)
outOfIndexArray = Array(suffix)
}
var array2D: [[String]] = stride(from: 0, to: target.count, by: stringsPerRow).map {
Array(target[$0..<$0+stringsPerRow])}
if !outOfIndexArray.isEmpty{
array2D.append(outOfIndexArray)
}
return array2D
}
I am trying to map a result to an angular-chart but am confused on how to map my labels and map my data. My service returns an object that looks like this:
Object {0: 5, 4: 1, 9: 1, 14: 2}
Question
How do I map my labels to the 0, 4, 9, 14 so that I can use it to put into a bar graph and data like below:
angular.module("app", ["chart.js"]).controller("BarCtrl", function ($scope) {
$scope.labels = ['0', '4', '9', '14'];
$scope.data = [
[5, 1, 1, 2]
];
});
Question 2 -- instead of using the numbers can I type in names for the labels?
I resolved this by mapping the value to an array.
var array = $.map($scope.count, function (value, index) {
return [value];
});
console.log(array);
Then using array on my chart.
Im trying to understand if there is a feature in ractivejs , for descending sorting , and ascending .
I couldnt find anyhting in the documentation .
No - Ractive purposely avoids being a 'kitchen sink' utility library. But it's very easy to add an ascending or descending helper:
var helpers = Ractive.defaults.data;
// assuming a and b are numbers...
helpers.ascending = function ( a, b ) {
return a - b;
};
helpers.descending = function ( a, b ) {
return b - a;
};
ractive = new Ractive({
el: 'body',
template: '' +
'<p>ascending: {{ numbers.slice().sort(ascending) }}</p>' +
'<p>descending: {{ numbers.slice().sort(descending) }}</p>'
},
data: {
numbers: [ 9, 4, 6, 2, 4, 1, 10, 2, 7, 8 ]
}
});
Note that you could also put the ascending and descending functions directly on the data object, if that's preferable.
Here's a JSFiddle to demonstrate: http://jsfiddle.net/rich_harris/nszt3150/