I have a long array of Booleans and I want to extract from it the index positions of TRUE followed by FALSE patterns. For example, this array
arrayOf(true, false, true, false, true, false)
should produce
[0, 2, 4]
but this one
arrayOf(true, false, false, false, true, false)
should produce
[0, 4]
Here's a fun for that except, of course, the 3rd example will crash Java:
fun main() {
println(dontCrash(arrayOf(true, false, true, false, true, false)))
println(dontCrash(arrayOf(true, false, false, false, true, false)))
println(dontCrash(arrayOf(true, false, false, false, true, true)))
}
fun dontCrash(a: Array<Boolean>): List<Int> {
val goodGuys = mutableListOf<Int>()
for ((ind, element) in a.withIndex())
if (element && !a[ind+1]) goodGuys.add(ind)
return goodGuys
}
[0, 2, 4]
[0, 4]
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 6 out of bounds for length 6
My question: Does Kotlin have some cute trick to deal this (step??) or do I need to add a .size() check?
windowed(2) or zipWithNext() lets you slide along pairs of adjacent values without going past the bounds, but it's only available for Iterables and Sequences, not arrays:
fun dontCrash(a: Array<Boolean>): List<Int> {
a.asSequence()
.zipWithNext()
.mapIndexedNotNull { i, (a, b) -> i.takeIf { a && !b } }
.toList()
}
Related
This question already has answers here:
dataweave transformation
(3 answers)
Closed 4 months ago.
Input :
{
"payload": {
"Field_X": "X",
"Field_Y": 10,
"Field_Z": "Z",
"Field_W" : {
"sub_1": "value_1",
"sub_2": true,
"sub_3": "value_3"
}
},
"mapper": {
"A": "<payload.Field_X>",
"B": "<payload.Field_Y>",
"C": "Fields or text not mapped to payload",
"D": {
"subD_1": "<payload.Field_W.sub_2>",
"subD_2": "<payload.Field_W.sub_4>"
}
}
}
Output
{
"A": "X",
"B": 10,
"C": "Fields or text not mapped to payload",
"D": {
"subD_1": true,
"subD_2": null
}
}
Note: Pls note that hardcoding is not allowed as per the client's request.
Every key-value pair has to be looped.
Based on my understanding of the original question, the following DataWeave script should accomplish the goal. I broke out the work into multiple functions so that it would be easier to follow.
%dw 2.4
output application/json
fun isExpression(value: String): Boolean = ((value startsWith("<")) and (value endsWith(">")))
fun extractExpression(value: String): Any = do {
if (isExpression(value))
dw::core::Strings::unwrap(value, "")
else value
}
fun evaluatePath(context: Object, expression: String): Any = do {
var tokens = expression splitBy "."
---
tokens reduce (val, acc = context) -> acc[val]
}
fun retrieveMappedValue(context: Object, value: String): Any = do {
var retValue = if (isExpression(value))
evaluatePath(context, extractExpression(value))
else value
---
retValue
}
fun remap(context: Object, object: Any, fn): Any = do {
if (object is Object)
entriesOf(object) reduce (val, acc = {}) -> dw::core::Objects::mergeWith(acc, {
(val.key): remap(context, val.value, fn)
})
else fn(context, object)
}
---
remap(payload, payload.mapper, retrieveMappedValue)
I have an array of object like below ,
[
{
"myValues": []
},
{
"myValues": [],
"values": [
{"a": "x", "b": "1"},
{"a": "y", "b": "2"}
],
"availableValues": [],
"selectedValues": []
}
]
also if i iterate the object, the "values" key present in the object, it will convert it into like below,
[
{
"myValues": []
},
{
"myValues": [],
"values": [
{"a": "x", "b": "1"},
{"a": "y", "b": "2"}
],
"availableValues": [
{"a": "x", "b": "1"},
{"a": "y", "b": "2"}
],
"selectedValues": ["x", "y"]
}
]
I tried with ramda functional programming but no result,
let findValues = x => {
if(R.has('values')(x)){
let availableValues = R.prop('values')(x);
let selectedValues = R.pluck('a')(availableValues);
R.map(R.evolve({
availableValues: availableValues,
selectedValues: selectedValues
}))
}
}
R.map(findValues, arrObj);
Immutability
First of all, you need to be careful with your verbs. Ramda will not modify your data structure. Immutability is central to Ramda, and to functional programming in general. If you're actually looking to mutate your data, Ramda will not be your toolkit. However, Ramda will transform it, and it can do so in a relatively memory-friendly way, reusing those parts of your data structure not themselves transformed.
Fixing your approach
Let's first look at cleaning up several problems in your function:
let findValues = x => {
if (R.has('values')(x)) {
let availableValues = R.prop('values')(x);
let selectedValues = R.pluck('a')(availableValues);
R.map(R.evolve({
availableValues: availableValues,
selectedValues: selectedValues
}))
}
}
The first, most obvious issue is that this function does not return anything. As mentioned above, Ramda does not mutate your data. So a function that does not return something is useless when supplied to Ramda's map. We can fix this by returning the result of the map(evolve) call and returning the original value when the if condition fails:
let findValues = x => {
if (R.has('values')(x)) {
let availableValues = R.prop('values')(x);
let selectedValues = R.pluck('a')(availableValues);
return R.map(R.evolve({
availableValues: availableValues,
selectedValues: selectedValues
}))
}
return x;
}
Next, the map call makes no sense. evolve already iterates the properties of the object. So we can remove that. But we also need to apply evolve to your input value:
let findValues = x => {
if (R.has('values')(x)){
let availableValues = R.prop('values')(x);
let selectedValues = R.pluck('a')(availableValues);
return R.evolve({
availableValues: availableValues,
selectedValues: selectedValues
}, x)
}
return x;
}
There's one more problem. Evolve expects an object containing functions that transform values. For instance,
evolve({
a: n => n * 10,
b: n => n + 5
})({a: 1, b: 2, c: 3}) //=> {a: 10, b: 7, c: 3}
Note that the values of the properties in the object supplied to evolve are functions. This code is supplying values. We can wrap those values with R.always, via availableValues: R.always(availableValues), but I think it simpler to use a lambda with a parameter of "_", which signifies that the parameter is unimportant: availableValues: _ => availableValues. You could also write availableValues: () => available values, but the underscore demonstrates the fact that the usual value is ignored.
Fixing this gets a function that works:
let findValues = x => {
if (R.has('values')(x)){
let availableValues = R.prop('values')(x);
let selectedValues = R.pluck('a')(availableValues);
return R.evolve({
availableValues: _ => availableValues,
selectedValues: _ => selectedValues
}, x)
}
return x;
}
let arrObj = [{myValues: []}, {availableValues: [], myValues: [], selectedValues: [], values: [{a: "x", b: "1"}, {a: "y", b: "2"}]}];
console.log(R.map(findValues, arrObj))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.js"></script>
If you run this snippet here, you will also see an interesting point, that the resulting availableValues property is a reference to the original values one, not a separate copy of it. This helps show that Ramda is reusing what it can of your original data. You can also see this by noting that R.map(findValues, arrObj)[0] === arrObj[0] //=> true.
Other Approaches
I wrote my own versions of this, not starting from yours. Here is one working function:
const findValues = when(
has('values'),
x => evolve({
availableValues: _ => prop('values', x),
selectedValues: _ => pluck('a', prop('values', x))
}, x)
)
Note the use of when, which captures the notion of "when this predicate is true for your value, use the following transformation; otherwise use your original value." We pass the predicate has('values'), essentially the same as above. Our transformation is similar to yours, using evolve, but it skips the temporary variables, at the minor cost of repeating the prop call.
Something still bothers me about this version. Using those lambdas with "_" is really a misuse of evolve. I think this is cleaner:
const findValues = when(
has('values'),
x => merge(x, {
availableValues: prop('values', x),
selectedValues: pluck('a', prop('values', x))
})
)
Here we use merge, which is a pure function version of Object.assign, adding the parameters of the second object to those of the first, replacing when they conflict.
This is what I would choose.
Anther solution
Since I started writing this answer, Scott Christopher posted another one, essentially
const findValues = R.map(R.when(R.has('values'), R.applySpec({
myValues: R.prop('myValues'),
values: R.prop('values'),
availableValues: R.prop('values'),
selectedValues: R.o(R.pluck('a'), R.prop('values'))
})))
This is a great solution. It also is nicely point-free. If your input objects are fixed to those properties, then I would choose this over my merge version. If your actual objects contain many more properties, or if there are dynamic properties that should be included in the output only if they are in the input, then I would choose my merge solution. The point is that Scott's solution is cleaner for this specific structure, but mine scales better to more complex objects.
Step-by-step
This is not a terribly complex transformation, but it is non-trivial. One simple way to build something like this is to build it up in very minor stages. I often do this in the Ramda REPL so I can see the results as I go.
My process looked something like this:
Step 1
Make sure I properly transform only the correct values:
const findValues = when(
has('values'),
always('foo')
)
map(findValues, arrObj) //=> [{myValues: []}, "foo"]
Step 2
Make sure my transformer function is given the proper values.
const findValues = when(
has('values'),
keys
)
map(findValues, arrObj)
//=> [{myValues: []}, ["myValues", "values", "availableValues", "selectedValues"]]
Here identity would work just as well as keys. Anything that demonstrates that the right object is passed would be fine.
Step 3
Test that merge will work properly here.
const findValues = when(
has('values'),
x => merge(x, {foo: 'bar'})
)
map(findValues, arrObj)
//=> [
// {myValues: []},
// {
// myValues: [],
// values: [{a: "x", b: "1"}, {a: "y", b: "2"}],
// availableValues: [],
// selectedValues: [],
// foo: "bar"
// }
// ]
So I can merge two objects properly. Note that this keeps all the existing properties of my original object.
Step 4
Now, actually transform the first value I want:
const findValues = when(
has('values'),
x => merge(x, {
availableValues: prop('values', x)
})
)
map(findValues, arrObj)
//=> [
// {myValues: []},
// {
// myValues: [],
// values: [{a: "x", b: "1"}, {a: "y", b: "2"}],
// availableValues: [{a: "x", b: "1"}, {a: "y", b: "2"}],
// selectedValues: []
// }
// ]
Step 5
This does what I want. So now add the other property:
const findValues = when(
has('values'),
x => merge(x, {
availableValues: prop('values', x),
selectedValues: pluck('a', prop('values', x))
})
)
map(findValues, arrObj)
//=> [
// {myValues: []},
// {
// myValues: [],
// values: [{a: "x", b: "1"}, a: "y", b: "2"}],
// availableValues: [{a: "x", b" "1"}, {a: "y", b: "2"}],
// selectedValues: ["x", "y"]
// }
// ]
And this finally gives the result I want.
This is a quick process. I can run and test very quickly in the REPL, iterating my solution until I get something that does what I want.
R.evolve allows you to update the values of an object by passing them individually to their corresponding function in the supplied object, though it doesn't allow you to reference the values of other properties.
However, R.applySpec similarly takes an object of functions and returns a new function that will pass its arguments to each of the functions supplied in the object to create a new object. This will allow you to reference other properties of your surrounding object.
We can also make use of R.when to apply some transformation only when it satisfies a given predicate.
const input = [
{
"myValues": []
},
{
"myValues": [],
"values": [
{"a": "x", "b": "1"},
{"a": "y", "b": "2"}
],
"availableValues": [],
"selectedValues": []
}
]
const fn = R.map(R.when(R.has('values'), R.applySpec({
myValues: R.prop('myValues'),
values: R.prop('values'),
availableValues: R.prop('values'),
selectedValues: R.o(R.pluck('a'), R.prop('values'))
})))
const expected = [
{
"myValues": []
},
{
"myValues": [],
"values": [
{"a": "x", "b": "1"},
{"a": "y", "b": "2"}
],
"availableValues": [
{"a": "x", "b": "1"},
{"a": "y", "b": "2"}
],
"selectedValues": ["x", "y"]
}
]
console.log(R.equals(fn(input), expected))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>
I am using datatables with statesave, colvis button to select the visibility of columns and rowcallback to put some colors on my data.
Here is my code:
activitiesTable = $('#activitiesTable').DataTable({
scrollX: true,
serverSide: true,
processing: true,
stateSave: true,
...
buttons: [
{
extend: "colvis",
className: "btn-sm",
columns: [ 1, 3, 4, 6, 7, 8, 9, 10, 12, 14, 16,18,20,22,24,26,28,30,32 ]
},
...
rowCallback: function(row, data, index){
if(data.jan_com<= 0){
$(row).find('td:eq(4)').addClass('zero');
}
else if(data.jan_otl> 0){
$(row).find('td:eq(4)').addClass('otl');
}
else {
$(row).find('td:eq(4)').addClass('forecast');
}
if(data.feb_com<= 0){
$(row).find('td:eq(5)').addClass('zero');
}
else if(data.feb_otl> 0){
$(row).find('td:eq(5)').addClass('otl');
}
else {
$(row).find('td:eq(5)').addClass('forecast');
}
...
So as you can see, I change the color of my data with class zero, forecast or otl.
The problem is that because it changes the columns that appear, the next time I reload, the colors are applied to the wrong column because column 4 is not anymore the column jan_com.
Is there a way to find the td corresponding to the column jan_com instead of asking td:eq(4)?
Thanks.
For my question I have prepared a simple test case at JS Bin.
In a word game I am trying to display the 20 longest words played by a player.
I deliver the data from PostgreSQL to DataTables jQuery plugin in JSON format. It is already sorted by word length and by the date when words were played.
This order is stored as numeric value (1, 2, 3, ...) in the row property of each JSON object:
var dataSet = [
{"row":4,"gid":1,"created":"25.02.2017 14:07","finished":null,"player1":2,"player2":1,"score1":30,"score2":52,"female1":0,"female2":0,"given1":"Abcde3","given2":"Ghijk4","photo1":null,"photo2":null,"place1":null,"place2":null,"word":"ZZ","score":11},
{"row":2,"gid":1,"created":"25.02.2017 14:07","finished":null,"player1":2,"player2":1,"score1":30,"score2":52,"female1":0,"female2":0,"given1":"Abcde3","given2":"Ghijk4","photo1":null,"photo2":null,"place1":null,"place2":null,"word":"BBBBB","score":6},
{"row":3,"gid":1,"created":"25.02.2017 14:07","finished":null,"player1":2,"player2":1,"score1":30,"score2":52,"female1":0,"female2":0,"given1":"Abcde3","given2":"Ghijk4","photo1":null,"photo2":null,"place1":null,"place2":null,"word":"ABC","score":7},
{"row":1,"gid":1,"created":"25.02.2017 14:07","finished":null,"player1":2,"player2":1,"score1":30,"score2":52,"female1":0,"female2":0,"given1":"Abcde3","given2":"Ghijk4","photo1":null,"photo2":null,"place1":null,"place2":null,"word":"XYZXYZXYZ","score":6}
];
Here is my JavaScript code, where I try to sort the column word (column 2) by the invisible column row (column 0):
function renderGid(data, type, row, meta) {
return (type === 'display' ? '<IMG SRC="https://datatables.net/examples/resources/details_open.png"> #' + data : data);
}
function renderGame(data) {
return 'Details for game #' + data.gid;
}
jQuery(document).ready(function($) {
var longestTable = $('#longest').DataTable({
data: dataSet,
order: [[2, 'desc']],
columns: [
{ data: 'row', orderable: false, visible: false },
{ data: 'gid', orderable: false, visible: true, className: 'details-control', render: renderGid },
{ data: 'word', orderable: true, visible: true, orderData: 0 /* order by invisible column 0 */ },
{ data: 'score', orderable: false, visible: true }
]
});
$('#longest tbody').on('click', 'td.details-control', function () {
var img = $(this).find('img');
var tr = $(this).closest('tr');
var row = longestTable.row(tr);
if (row.child.isShown()) {
row.child.hide();
img.attr('src', 'https://datatables.net/examples/resources/details_open.png');
} else {
row.child( renderGame(row.data()) ).show();
img.attr('src', 'https://datatables.net/examples/resources/details_close.png');
}
});
});
However this does not work - the displayed words order is ZZ, BBBB, ABC, XYZXYZXYZ (seemingly unsorted) - while it should be XYZXYZXYZ, BBBB, ABC, ZZ (sorted by row descending):
Why does not sorting work even though I have specified columns.orderData: 0?
And why can't I change ordering by clicking the greyed out arrows (shown by the red arrow in the above screenshot)?
Ok, this seems to be an old bug in dataTables jQuery plugin: the integer argument is not accepted.
I have to change it to an array with the single value:
{ data: 'word', orderable: true, visible: true, orderData: [0] },
and then it works:
I am using the Datatables plugin with the Autofill extension with input elements as outlined here:
DataTables' Autofill extension with input elements not working.
This works well. However, I am unable to disable the autofill for specific columns. When I use the "enable": false option, and set it to specific columns, then the callbacks stop working. Does anyone know if there is a way to disable certain columns for autofill, while still allowing the callbacks to function properly? The following disables cols 1-4, but the read/write/step functions no longer copy edited input values:
new $.fn.dataTable.AutoFill(table, {
"columnDefs": [{
"targets": [5, 6, 7, 8, 9],
"read": function (cell) {
return $('input', cell).val();
},
"write": function (cell, val) {
return $('input', cell).val(val);
},
"step": function (cell, read, last, i, x, y) {
return last === undefined ? read : last;
},
"enable": false, "targets": [1,2,3,4] //omitting this leaves all columns enabled.
}]
});
The way you've written it, you are defining the targets property twice in the same object. What you need to do is to give columnDefs another object pointing to the other targets. Like so:
new $.fn.dataTable.AutoFill(table, {
columnDefs: [
{
targets: [5, 6, 7, 8, 9],
read: function (cell) {
return $('input', cell).val();
},
write: function (cell, val) {
return $('input', cell).val(val);
},
step: function (cell, read, last, i, x, y) {
return last === undefined ? read : last;
}
},
{
targets: [1,2,3,4],
enable: false
}
]
});