In the AngularJS doc for "orderBy", the description of the "string" property is as follows (with my emphasis):
An Angular expression which evaluates to an object to order by, such as 'name' to sort by a property called 'name'. Optionally prefixed with + or - to control ascending or descending sort order (for example, +name or -name).
The description of the "reverse" property is as follows:
Reverse the order the array.
So what should be the difference between using "-property" and "reverse"? Should both of these do the same thing?
For my test case, I can't get "reverse" to do anything. I constructed a plunkr with three ngRepeats. The first one is without any ordering, the second one ordered with "reverse", and the last ordered with "-property". The latter does what I expect, the former doesn't appear to do anything.
The documentation is clearly ambiguous, but I would have expected "reverse" and "-property" to do the same thing. What's going on here?
Looking at the manual you'll find reverse to be of type boolean. Which indicates that it's value will be looked up in the current scope or is meant to be literal. Simply plucking in reverse will then evaluate to undefined (because it's probably not defined on the scope) which is falsy and thus reverse has no effect. Use a scope value or a litteral (true, false) to see it's effect.
The reason to have two ways of reversing the sort order is because it's hard to to change the +/- through data-binding techniques, while it's easy to simply refer to a scope variable using the reverse expression. And thus change the order through data-binding (e.g. button clicks which change a scope variable, which is actually an example on the manual page).
Instead of orderBy:'lastRun':reverse put quotes around 'reverse': orderBy:'lastRun':'reverse'
Related
I am trying this out but I cant seem to get it to work.
In a Condition connector I'm doing this:
#contains(json(body('ParseCustomerDeltaXML')).newMembers[0], 'Member')
but i cant get it to work.
If it contains members it says true.
But if not i get an error:
InvalidTemplate. Unable to process template language expressions for action 'Condition' at line '1' and column '2706': 'The template language expression 'equals(json(body('ParseCustomerDeltaXML')).newMembers[0], null)' cannot be evaluated because array index '0' cannot be selected from empty array. Please see https://aka.ms/logicexpressions for usage details.'.
As indicated by the error message, the array you are trying to reference the first item of is empty. You want to make use of the safe dereference operator .?
Suppose newMembers is an empty array. Then newMembers[0] would fail, but newMembers?[0] would succeed (and return null).
In the specific scenario you are describing, you may need to use a nested condition as well (i.e. first check if newMembers is non-empty, and then check for membership).
To check for emptiness you can use the #empty() expression.
In my example I should check is element empty before fetching street data from element.
This works:
if(empty(body('Parse_JSON')?['results'][0]['addresses']), '', body('Parse_JSON')?['results'][0]['addresses'][0]['street'])
and this works too:
if(contains(['addresses'], ['addresses']?[0]), 'Do something', 'Or do this thing')
Hope that will help someone.
Please consider the following strings array:
let strings = ["str1", "str2", "str10", "str20"]
Let's assume that what required is to get the first element (String) which contains 5 characters, I could get it by using filter(_:) as follows:
let filterString = strings.filter { $0.count == 5 }.first
print(filterString!) // str10
but after reviewing the first(where:) method, I recognized that I will be able to get the same output:
let firstWhereString = strings.first(where: { $0.count == 5 })
print(firstWhereString!) // str10
So what is the benefit of using one instead of the other? is it only about that the filter(_:) returns a sequence and the first(where:) returns a single element?
Update:
I noticed that the filter(_:) took 5 times to do such a process, while first(where:) took 4 times:
You are correct in observing that filter(_:) returns all elements that satisfy a predicate and that first(where:) returns the first element that satisfy a predicate.
So, that leaves us with the more interesting question of what the difference is between elements.filter(predicate).first and
elements.first(where: predicate).
As you've already noticed they both end up with the same result. The difference is in their "evaluation strategy". Calling:
elements.filter(predicate).first
will "eagerly" check the predicate against all elements to filter the full list of elements, and then pick the first element from the filterer list. By comparison, calling:
elements.first(where: predicate)
will "lazily" check the predicate against the elements until it finds one that satisfies the predicate, and then return that element.
As a third alternative, you can explicitly use "a view onto [the list] that provides lazy implementations of normally eager operations, such as map and filter":
elements.lazy.filter(predicate).first
This changes the evaluation strategy to be "lazy". In fact, it's so lazy that just calling elements.lazy.filter(predicate) won't check the predicate against any elements. Only when the first element is "eagerly" evaluated on this lazy view will it evaluate enough elements to return one result.
Separately from any technical differences between these alternatives, I'd say that you should use the one that most clearly describes your intentions. If you're looking for the first element that matches a criteria/predicate then first(where:) communicates that intent best.
I believe we should start from considering each method separately and their purpose.
filter(_:) is not purposefully designed to prepare us to obtain first element. It is about filtering and that's it. It merely returns us a sequence. first can be used after filter, but that's just an instance of usage, a possible case. first is called for a collection and if we want, of course we are free to use it directly after filter.
But first(where:) was designed to filter and return a single element and is evidently the go-to approach to accomplish that kind of a task. That said, it is easy to presume that it's also preferred from performance perspective. As mentioned, we filter entire sequence, but with first(where:) we only process the portion of a sequence until condition is met.
Sometimes you need to ng-if or ng-show an item in html based on some choices made earlier. One of these for me is "Additional Item". You can enter one set of information, and also if you want, an additional set. This creates an array of 2 similar objects. With this setup, you can only have 1 or 2 objects in this array. (important, since the scope of this question needs to be limited this way)
I want to ng-show an html directive based on "myItemsArray.length > 1". Since the array can (read should) only be 1 or 2 in length (not 0), this should work. However, it does not, because AngularJS seems to be adding an item "proto" to the array which adds to the count. See the image.
The problem is, proto makes the array length equal 2. I am not going to just look for length > 2 because i really don't know if i can count on proto always being there, and i just think thats bad practice anyway.
Also, i know there are MANY other ways of doing this (setting a boolean, or using another var to indicate etc, but i really just want to work with count of items in the array because "business logic"..
EDIT:
After doing a little debugging, i'm seeing that i have an array of "Object, undefined". How is this even possible :)
Some search lead me to this. Why are some values in my array undefined
EDIT:
Seems that using a delete may cause this problem
I'm a bit confused after reading Angular's orderBy documentation:
In HTML Template Binding:
{{ orderBy_expression | orderBy : array : expression : reverse}}`
This shows orderBy being used with 3 additional parameters (reverse is listed as optional), but I cannot find any examples of it being used with more than 2, and when it is 2, it appears to be in the form {{ orderBy_expression | orderBy : expression : reverse}} (ommitting array)
array is defined as "The array to sort.", but what does that make orderBy_expression? Shouldn't that be the array the filter acts upon?
I was actually going to Improve this doc and modify this (what I assume is a documentation error), but it wasn't at all clear to me what exactly was generating the template binding example (the docs are generated with JavaDoc-like comments right in the .js)
So, hopefully this is a valid SO question:
Is the documentation in fact incorrect? Or am I somehow confused
Filters have 2 modes of use. Programmatically, as a function, in which case the first param IS the array to act upon, and inline with | where the array on the left hand side is the array to act upon. So while it may not be immediately clear that is what is going on, the docs are not incorrect. Not saying it shouldn't be cleaned up. It would certainly be nicer if they showed both modes and clearly explained it. But I still stand by it being "correct." And, as Mikke pointed out, the current style of explanation IS consistent through the docs.
The code I am playing around with can be found here.
As of now, in all of my text fields, ng-model has only one name, fieldData. When I take the created javascript object and make it into a JSON object, I get the following:
[{"pHolder":"ID goes here","fieldData":"123"},{"pHolder":"Description goes here","fieldData":"456"},{"pHolder":"Drop Dead Date goes here","fieldData":"789"}]
Since each field has a different meaning, I would like that to be reflected in the bound name.
So instead of an array with three objects that each have the string called fieldData, I would like an array of three objects where foo bar and baz are substituted in each place where there is now fieldData.
How do I do that?
Javascript Array objects are quite flexible, as any other aspect of it, and you can use that to your advantage :) ngRepeat iterates through any array, be it ordered or associative (which are basically the same). So, if you change your array to:
$scope.entryFields = {
id: {pHolder:'ID goes here',fieldData:""},
description: {pHolder:'Description goes here',fieldData:""},
date: {pHolder:'Drop Dead Date goes here',fieldData:""}
};
The ng-repeat element will still work (you may want to reorder it, as it orders alphabetically by key name, by default) without any changes, but you can adress the fields by name (dot notation). See the updated fiddle here.