ES2015 - updating filtered items in a collection and combining updating the previous collection - functional-programming

So say i have a collection:
const collection = [0,3,2,4];
var r = collection.filter((item) => {
return item > 2;
}).map((item) => {
return item + 2;
});
// I could just `.map()` them back in but that seems ineffective
console.log(r); // 5,6
Is there a functional (fp) way to compose this code to update my previous collection to get the expected result (given the index of the result is important) ?
e.g. expected result: [0,5,2,6]

How about
var r = collection.map(item => item > 2 ? item + 2 : item)
instead?
I.e. .map all items but only modify the ones that match your condition.

Related

How to take text from elements to use it later in cypress?

I need to take 2 lists of text values on page to use each of them in different cheking.
I do it by xpath
cy.xpath('//tbody/tr[position()<10]/td[6]/span').then(items => {
cy.wrap(items).as('multipleList')
})
cy.xpath('//tbody/tr[position()<21][position()>15]/td[6]/span').then(items => {
cy.wrap(items).as('commaList')
})
cy.get('#multipleList').each((qwer, index) => {..........})
cy.get('#commaList').each((qwer, index) => {..........})
But after using the first list (#multipleList), it shows error that the second list (#commaList) is empty.
If I swap them, then #commaList is being executed OK, but #multipleList shows the same error.
Try a combined selection and iteration only once
cy.xpath('//tbody/tr').as('list')
cy.get('#list').each(($rows, rowIndex) => {
if (rowIndex < 9) {
const $span = $rows[index].find('td:eq(6) span')
...
}
if (rowIndex > 14 && rowIndex < 20) {
const $span = $rows[index].find('td:eq(6) span')
...
}
})
With the test as you've shown, it's not possible.
If you have an error it's the processing in .each() causing page changes (2nd list becomes invalid).
Try to reorder the commands
cy.xpath('//tbody/tr[position()<10]/td[6]/span').as('multipleList')
cy.get('#multipleList').each((qwer, index) => {..........})
cy.xpath('//tbody/tr[position()<21][position()>15]/td[6]/span').as('commaList')
cy.get('#commaList').each((qwer, index) => {..........})

Retrieve and compare the style attribute of an element periodically using using cypress

I have a time indicator that travels over a timescale, the indicator's style attribute value keeps on changing for every x milliseconds and I need to get, store and compare that the previously captured value is greater than the latest value.
Initial value:
Latest value:
The logic is, from one point (left 10), every second it moves to the left (left -0, -1, -2, -3 ...)
I tried few ways and one of them is to capture in the same 'cy.then', but in that case, the element will not have the recent value. So far, I tried this. it fetches the value and with some help of regex, I got a 'comparable' value but how I can store/compare those values? Additionally, what is the best way if we need to compare more than 2 values?
const BTN_CONTROL_TIMEINDICATOR = '#currentTimeIndicator'
static verifyTimeLapse() {
//wip
var initialVal, nextVal
initialVal = this.getAnyValueOfAnElement(BTN_CONTROL_TIMEINDICATOR)
cy.wait(500)
nextVal = this.getAnyValueOfAnElement(BTN_CONTROL_TIMEINDICATOR)
cy.log(initialVal > nextVal)
}
static getAnyValueOfAnElement(element) {
//wip
cy.get(element)
.then(($ele) => {
const val=$ele.attr('style').replace(/[^\d.-]/g, '')
cy.log(val)
// return does not work
})
}
cy.log:
Page objects don't work very well with the Cypress command queue, here's what you might do with custom commands.
/* Get the numeric value of CSS left in px */
Cypress.Commands.add('getTimescaleValue', () => {
cy.get('#currentTimeIndicator')
.then($el => +$el[0].style.left.replace('px',''))
})
/* Get a sequence of time scale values */
Cypress.Commands.add('getTimescaleValues', ({numValues, waitBetween}) => {
const values = [];
Cypress._.times(numValues, () => { // repeat inner commands n times
cy.getTimescaleValue()
.then(value => values.push(value)) // save value
.wait(waitBetween)
})
return cy.wrap(values);
})
/* Assert a sequence of values are in descending order */
Cypress.Commands.add('valuesAreDescending', { prevSubject: true }, (values) => {
values.reduce((prev, current) => {
if (prev) { // skip first (no prev to compare)
expect(prev).to.be.gt(current) // assert pairs of values
}
return current
});
})
it('check the timeline', () => {
cy.getTimescaleValues({ numValues: 10, waitBetween: 100 })
.valuesAreDescending()
})
Log
assert
expected 63 to be above 58
assert
expected 58 to be above 48
assert
expected 48 to be above 43
assert
expected 43 to be above 33
assert
expected 33 to be above 23
assert
expected 23 to be above 18
assert
expected 18 to be above 13
assert
expected 13 to be above 3
assert
expected 3 to be above -2
Tested with
<div id="currentTimeIndicator" style="left:63px">Target</div>
<script>
const timer = setInterval(() => {
const div = document.querySelector('#currentTimeIndicator')
const left = +div.style.left.replace('px', '');
if (left < 0) {
clearInterval(timer)
return
}
const next = (left - 5) + 'px';
div.style.left = next;
}, 100)
</script>
If your app uses setInterval() for timing, you should be able to use cy.clock() and cy.tick() instead of .wait(waitBetween) to get more precise sampling and faster test execution.
I don't know where the initial value comes from. But before it changes, maybe on page load, maybe as first job on click, etc you can do something like this:
let item = document.querySelector("#currentTimeIndicator");
item.dataset.left = parseFloat(item.style.left);
console.log(item);
<div id="currentTimeIndicator" style="left:-20px"></div>

How to add new item in specific index?

I new in kotlin , i want to update an item in lists.
I use this code:
var index: Int
for (record in recordList)
if (record.id == updatedHeader?.id) {
index = recordList.indexOf(record)
recordList.add(index, updatedHeader)
}
but it cant do this, because of ConcurrentModificationException
Assuming that recordList is a MutableList and val (so, you'd like to modify the records in place), you can use forEachIndexed to find the records you care about and replace them.
This did not cause a ConcurrentModificationException:
recordList.forEachIndexed { index, record ->
if(record.id == updatedHeader?.id) recordList[index] = updatedHeader
}
On the other hand, if you redefine recordList as a non-mutable list, and a var, you could rewrite the entire list using map:
recordList = recordList.map { if(it.id == updatedHeader?.id) updatedHeader else it }
Of course, you could call .toMutableList() on the end of that if you wanted to turn your List into a MutableList.
If there's a single record with the given id in the list, you can find its index and add the header at that index:
val index = recordList.indexOfFirst { it.id == updatedHeader.id }
if (index >= 0)
recordList.add(index, updatedHeader)
If there are multiple records with the given id and you want to prepend header before each of them, you can use get listIterator and use its methods to modify the list during the iteration without getting ConcurrentModificationException:
val iterator = recordList.listIterator()
for (record in iterator) {
if (record.id == updatedHeader.id) {
iterator.previous() // move to the position before the record
iterator.add(updatedHeader) // prepend header
iterator.next() // move next, back to the record
}
}

Telerik Extensions for ASP.NET MVC - GRID - randomly sorted items inside group in Chrome when GridOperationMode.Client

Data source contains many records, but each 12 records represent 12 characteristic of 1 entity sorted in fixed order. Then rows are grouped by 3 columns (by 'AccountName', 'OportunityName' and 'OpportunityId'), and the group on deepest level contains those 12 characteristic. All worked OK when 'GridOperationMode.Server' was used:
But to increase performance we decided to change operation mode to client - 'GridOperationMode.Client'. After that performance became better, but those 12 characteristic lost they sorting in Chrome - for each group they are rendered in random order. I checked the issue in IE and FF - and found that they don't have such a problem. Any ideas how to fix the wrong order in chrome?
Wrong order in Chrome when GridOperationMode.Client is used
JS (shortened) - binds grid:
function populateForecastClosedGrid(controllerActionUrl) {
var gridForecastClosed = $("#gridFORECASTREPORT").data("tGrid");
var accountId = $('#accountsFilterCombo').data('tComboBox').value();
gridForecastClosed.ajax.selectUrl = controllerActionUrl + '?checkDate=' + new Date().formatMMDDYYYY() + '&accountId=' + accountId;
gridForecastClosed.showBusy();
$.post(gridForecastClosed.ajax.selectUrl, function (data) {
gridForecastClosed.dataSource.data([]);;
gridForecastClosed.dataBind(data);
});
}
Grid (shortened):
#(Html.Telerik().Grid()
.Name("gridFORECASTREPORT")
.Columns(columns => { ... }
.DataKeys(keys => keys.Add(c => c.OpportunityId))
.DataBinding(dataBinding => dataBinding.Ajax().OperationMode(GridOperationMode.Client))
.Groupable(grouping => grouping.Groups(groups =>
{
groups.Add(c => c.AccountName);
groups.Add(c => c.OpportunityName);
groups.Add(c => c.OpportunityId);
}).Visible(false))
.EnableCustomBinding(true)
.Pageable(p => p.PageSize(396)))
After a lot of researching I decided to implement sorting by myself using JS.
Works fast for page size equals 396 my grid uses, of course can be made faster. Each from those linked 12 items already has a field SortOrder with correct order inside this 12-items group. Quick and dirty, enjoy! If you know better solution please share. So far marked as answered. Really working solution, approved by my TL as no other ways found, can be adapted to any other grid.
function onGridForecastClosedDataBound() {
var grid = $(this).data('tGrid');
// Request came to increase Forecast (Closed) grid performance. The only way (w/o touching SQL)
// I found is to change grid operation mode from Server to GridOperationMode.Client (~50% increase).
// But Telerik Grid + Chrome (OK on IE, FF) has a problem - wrong sorted items inside group
// when grouping is performed on client side. This is a quick and dirty workaround for this
// particular grid - to perform "sorting" manually using JS.
// IMPORTANT! Pay attention, that if you change number of rows per
// opportunity (currently 12) then the grid will be broken w/o changing the code below.
if ('#Request.Browser.Browser' == 'Chrome') {
var numberOfRowsPerOpportunity = 12;
var rows = grid.$tbody.find('tr');
var rowsSorted = [];
while (rows.length > 0) {
var partGroups = rows.splice(0, rows.slice(0, grid.groups.length).filter('.t-grouping-row').length);
var partRows = rows.splice(0, numberOfRowsPerOpportunity);
partRows.sort(function (a, b) {
var sortOrderA = parseInt($(a).find('td.sort-order').text());
var sortOrderB = parseInt($(b).find('td.sort-order').text());
return sortOrderA - sortOrderB;
});
$.each(partRows, function (index, item) {
$(item).removeClass('t-alt');
if (index % 2 != 0) $(item).addClass('t-alt');
});
$.merge(rowsSorted, partGroups);
$.merge(rowsSorted, partRows);
}
rows.remove();
grid.$tbody.append(rowsSorted);
}
grid.hideBusy();
}
function populateForecastClosedGrid(controllerActionUrl) {
var gridForecastClosed = $("#gridFORECASTREPORT").data("tGrid");
var accountId = $('#accountsFilterCombo').data('tComboBox').value();
gridForecastClosed.ajax.selectUrl = controllerActionUrl + '?checkDate=' + new Date().formatMMDDYYYY() + '&accountId=' + accountId;
gridForecastClosed.showBusy();
gridForecastClosed.dataSource.data([]);
gridForecastClosed.ajaxRequest();
}
Grid (shortened):
#(Html.Telerik().Grid<mForecastReport>()
.Name("gridFORECASTREPORT")
.DataKeys(keys => keys.Add(c => c.OpportunityId))
.ClientEvents(e => e.OnDataBound("onGridForecastClosedDataBound"))
.DataBinding(dataBinding => dataBinding.Ajax().OperationMode(GridOperationMode.Client))
.Columns(columns => {
...
columns.Bound(c => c.SortOrder).Hidden(true).HtmlAttributes(new { #class = "sort-order" });
}
.Groupable(grouping => grouping.Groups(groups =>
{
groups.Add(c => c.AccountName);
groups.Add(c => c.OpportunityName);
groups.Add(c => c.OpportunityId);
}).Visible(false))
.Pageable(p => p.PageSize(396)))

Filtering a Backbone.js collection by index

I have Backbone.js collection that holds (for example) 30 items.
I want to pass to my template filtered collection consist of every 3rd item in the original collection.
Does anyone know how it can be done elegantly? CoffeeScript code is preferred.
Assuming here that originalCollection is your existing collection
var newCollection = new Backbone.Collection();
for (var i = 0, l = originalCollection.length; i < l; i++) {
if (i % 3 === 0) { newCollection.add(originalCollection.models[i]); }
}
This code works by looping through each existing model, and only adding the model the new collection if it's index is a multiple of 3.
You could make this a little nicer, by using the underscore each method exposed by Underscore.js in Backbone Collections:
var newCollection = new Backbone.Collection();
originalCollection.each(function (model, index) {
if (index % 3 === 0) { newCollection.add(model); }
});
Converting the above to CoffeeScript results in:
newCollection = new Backbone.Collection()
originalCollection.each (model, index) ->
newCollection.add model if index % 3 is 0
Backbone collection have some useful Underscore methods mixed-in. You can use filter to get an array of models that you can pass to the template:
filteredModels = collection.filter (model, i) -> i % 3 == 0
Alternatively, you can use an array comprehension; though I think this is less readable...
filteredModels = (model for model, i in collection.models when i % 3 == 0)
If you really need a Backbone.Collection in your template, you can create a new one with those filtered models:
filteredCollection = new Backbone.Collection filteredModels
Here is a working jsfiddle example.

Resources