I have data that is in an array of structs. I don't have access to a query variable, and I need to filter down the data. So var I have
arData = arData.filter(
function(item){
return (form.searchPhrase == ""
||
item.name CONTAINS form.searchPhrase
||
item.company CONTAINS form.searchPhrase
||
item.address CONTAINS form.searchPhrase
||
item.address2 CONTAINS form.searchPhrase
||
item.city CONTAINS form.searchPhrase
||
item.state CONTAINS form.searchPhrase
||
item.zip CONTAINS form.searchPhrase
||
item.email CONTAINS form.searchPhrase
||
item.tel CONTAINS form.searchPhrase
);
});
I am going over all the fields in item.
What I don't like about this is that it seems highly repetitive. It is likely that a new struct key could be added. Or an existing be removed. I am looking for a cleaner way to do this.
Why not just loop over the item's properties then?
arData = arData.filter(
function(item){
if (form.searchPhrase == "") {
return true;
}
for (itemProperty in item){
if (isSimpleValue(item[itemProperty]) && item[itemProperty] CONTAINS form.searchPhrase) {
return true;
}
}
return false;
});
In case your item might contain properties with a null value (for example if you read JSON from an external API), you want to check structKeyExists(item, itemProperty) in the loop as well.
Related
I have a webform with a file upload field. I need do one of two things. Either upload files into a subfolder inside the private area, OR, add a prefix to the filename that the user is uploading. The user can upload several files. The webform editor allows you to 'Rename' the file and use tokens for this, but I don't see any way of preserving the original file name. I can hack getFileDestinationUri() in WebformManagedFileBase.php to do what I want, but obviously I would rather not do that. Am I missing something?
It turns out that you can use the drupal form alter hook to alter the destination. I had thought of this but felt if was unlikely to work. It does.
Below is MY code solution: (I may be missing container types)
function _mymodule_fix_elements(&$elements) {
foreach ($elements as $key => &$element) {
if (strpos($key, '#') !== 0) {
if (is_array($element)) {
if (isset($element['#type'])) {
if (($element['#type'] == 'fieldset') ||
($element['#type'] == 'webform_flexbox') ||
($element['#type'] == 'container')) {
_mymodule_fix_elements($element);
} else if ($element['#type'] == 'managed_file') {
$pattern = $element['#upload_location'];
if (strpos($pattern, 'private:') === 0) {
$element['#upload_location'] = $pattern . '/' . $key;
}
}
}
}
}
}
}
function mymodule_form_alter(&$form, &$form_state, $id) {
if (strpos($id, 'webform_') === 0) {
_mymodule_fix_elements($form['elements']);
}
}
I tried to edit a list in-place but wasn't able to get it working. Now trying to edit individual elements and add them to a second list. However the second list remains null and does not get updated. Any help would be appreciated.
var localFiles: MutableList<String> = File(localPath).list().toMutableList()
var localFileDates: MutableList<String>? = null
val iterateLocal = localFileDates?.listIterator()
for (item in localFiles) {
var date = item.takeLast(10).take(6)
if (date.matches("[0-9]+".toRegex()) and (date.length == 6) and (date != null) and (date != "null")) {
iterateLocal?.add(item.takeLast(10).take(6))
}
}
println(networkFiles) // prints correct outpu
println(localFileDates) // prints null
You need init localFileDates variable:
var localFileDates = MutableList()
var localFiles: MutableList<String> = File(localPath).list().toMutableList()
var localFileDates = MutableList<String>()
val iterateLocal = localFileDates?.listIterator()
for (item in localFiles) {
var date = item.takeLast(10).take(6)
if (date.matches("[0-9]+".toRegex()) and (date.length == 6) and (date != null) and (date != "null")) {
iterateLocal?.add(item.takeLast(10).take(6))
}
}
println(networkFiles) // prints correct outpu
println(localFileDates) // prints correct
It is better to use map{..} function to create a copy of the list with updated values.
val localFiles = File(localPath).list()?.toMutableList() ?: listOf()
val localFileDates = localFiles.mapNotNull { item ->
val date = item.takeLast(10).take(6)
if (date.matches("[0-9]{6}".toRegex()) {
date
} else {
null
}
}
println(localFiles)
println(localFileDates)
I use the mapNotNull{..} function calls the block for every element of the list and builds the new list only from non-null values.
You do not need var in your code, explicit type names can be omitted too.
The condition can be simplified - no need for the null check, the regular expression filters our the data == "null" case, the length check can be included into the regex too. The date variable can be re-used too.
A more idiomatic (and readable) way:
val localFileDates = File(localPath).list().map { it.takeLast(10).take(6) }.filter {
it.matches("[0-9]+".toRegex()) && (it.length == 6) && (it != "null")
}
Also I suggest you create a named function for takeLast(10).take(6) and the condition to make it clear what is the intent of these lines of code.
This is my code for true on everything but empty string, null and false:
if (routeinfo["no_route"] == "" || routeinfo["no_route"] == null || routeinfo["no_route"] == false) {
// do sth ...
}
This is my code for true on everything but empty string, null, false or zero:
if (routeinfo["no_route"] == "" || routeinfo["no_route"] == null || routeinfo["no_route"] == false || routeinfo["no_route"] == 0) {
// do sth...
}
How can I write this shorter in Dart? Or is it not possible?
If your requirement was simply empty or null (like mine when I saw this title in a search result), you can use Dart's safe navigation operator to make it a bit more terse:
if (routeinfo["no_route"]?.isEmpty ?? true) {
//
}
Where
isEmpty checks for an empty String, but if routeinfo is null you can't call isEmpty on null, so we check for null with
?. safe navigation operator which will only call isEmpty when the object is not null and produce null otherwise. So we just need to check for null with
?? null coalescing operator
If your map is a nullable type then you have to safely navigate that:
if (routeinfo?["no_route"]?.isEmpty ?? true) {
//
}
You could do
if (["", null, false, 0].contains(routeinfo["no_route"])) {
// do sth
}
Late 2020 Update
Summary
This answer holds true, except for isNull and isNotNull. They no longer provide type promotion when Null Safety is introduced in dart/flutter in the future.
Other helpers like isNullOrEmpty do not provide type promotion, as they are in a different (sub-)scope compared to callsite.
My personal opinion, is that you can drop isNull and isNotNull but keep other helpers as you shouldn't expect them to do type promotion for you.
Explanation
This is because Null Safety was finally introduced in Dart/Flutter. But as of October 2020, this feature is still not available for stable releases on dart/flutter. Check out the quick guide Null Safety or the thorough Understanding Null Safety.
Null Safety in Dart/Flutter should be similar to Swift (Optional) and Kotlin (Nullable Types, Non-Nullable Types).
Demonstration
Here's a demonstration why encapsulation/helper-getter of isNull (== null) and isNotNull (!= null) is a very big problem:
// Promotion works
int definitelyInt(int? aNullableInt) {
if (aNullableInt == null) { // Promote variable `aNullableInt` of Nullable type `int?` to Non-Nullable type `int`
return 0;
}
return aNullableInt; // Can't be null! This variable is promoted to non-nullable type `int`
}
When "Null Safety" is shipped in the dart release you are using, the type promotion in above code works! HOWEVER:
// Promotion does NOT work!!!
int definitelyInt(int? aNullableInt) {
if (aNullableInt.isNull) { // does NOT promote variable `aNullableInt` of Nullable type `int?`
return 0;
}
return aNullableInt; // This variable is still of type `int?`!!!
}
The above doesn't work, because the null check == null and != null are encapsulated in a sub-scope (different stack frame) and not inferred to have this "promotion" effect within definitelyInt scope.
Early 2020 Update
Here's a modified version using getters instead of instance/class methods and covering whitespaces, isNull, and isNotNull.
Second Update
It also accounts for empty lists and maps now!
Third Update
Added private getters for safety and modularity/maintainability.
Fourth Update
Updated the answer to fix a particular problem with Map, it's not correctly being handled when empty! Because Map is not an Iterable. Solved this issue by introducing _isMapObjectEmpty U
Solution
extension TextUtilsStringExtension on String {
/// Returns true if string is:
/// - null
/// - empty
/// - whitespace string.
///
/// Characters considered "whitespace" are listed [here](https://stackoverflow.com/a/59826129/10830091).
bool get isNullEmptyOrWhitespace =>
this == null || this.isEmpty || this.trim().isEmpty;
}
/// - [isNullOrEmpty], [isNullEmptyOrFalse], [isNullEmptyZeroOrFalse] are from [this StackOverflow answer](https://stackoverflow.com/a/59826129/10830091)
extension GeneralUtilsObjectExtension on Object {
/// Returns true if object is:
/// - null `Object`
bool get isNull => this == null;
/// Returns true if object is NOT:
/// - null `Object`
bool get isNotNull => this != null;
/// Returns true if object is:
/// - null `Object`
/// - empty `String`s
/// - empty `Iterable` (list, set, ...)
/// - empty `Map`
bool get isNullOrEmpty =>
isNull ||
_isStringObjectEmpty ||
_isIterableObjectEmpty ||
_isMapObjectEmpty;
/// Returns true if object is:
/// - null `Object`
/// - empty `String`
/// - empty `Iterable` (list, map, set, ...)
/// - false `bool`
bool get isNullEmptyOrFalse =>
isNull ||
_isStringObjectEmpty ||
_isIterableObjectEmpty ||
_isMapObjectEmpty ||
_isBoolObjectFalse;
/// Returns true if object is:
/// - null `Object`
/// - empty `String`
/// - empty `Iterable` (list, map, set, ...)
/// - false `bool`
/// - zero `num`
bool get isNullEmptyFalseOrZero =>
isNull ||
_isStringObjectEmpty ||
_isIterableObjectEmpty ||
_isMapObjectEmpty ||
_isBoolObjectFalse ||
_isNumObjectZero;
// ------- PRIVATE EXTENSION HELPERS -------
/// **Private helper**
///
/// If `String` object, return String's method `isEmpty`
///
/// Otherwise return `false` to not affect logical-OR expression. As `false` denotes undefined or N/A since object is not `String`
bool get _isStringObjectEmpty =>
(this is String) ? (this as String).isEmpty : false;
/// **Private helper**
///
/// If `Iterable` object, return Iterable's method `isEmpty`
///
/// Otherwise return `false` to not affect logical-OR expression. As `false` denotes undefined or N/A since object is not `Iterable`
bool get _isIterableObjectEmpty =>
(this is Iterable) ? (this as Iterable).isEmpty : false;
/// **Private helper**
///
/// If `Map` object, return Map's method `isEmpty`
///
/// Otherwise return `false` to not affect logical-OR expression. As `false` denotes undefined or N/A since object is not `Map`
bool get _isMapObjectEmpty => (this is Map) ? (this as Map).isEmpty : false;
/// **Private helper**
///
/// If `bool` object, return `isFalse` expression
///
/// Otherwise return `false` to not affect logical-OR expression. As `false` denotes undefined or N/A since object is not `bool`
bool get _isBoolObjectFalse =>
(this is bool) ? (this as bool) == false : false;
/// **Private helper**
///
/// If `num` object, return `isZero` expression
///
/// Otherwise return `false` to not affect logical-OR expression. As `false` denotes undefined or N/A since object is not `num`
bool get _isNumObjectZero => (this is num) ? (this as num) == 0 : false;
}
Assumptions
This presumes Dart 2.7 or above to support Extension Methods.
Usage
The above are two Extension classes. One for Object and one for String. Put them in a file separately/together and import the files/file at callsite file.
Description
Coming from Swift, I tend to use extensions like user #Benjamin Menrad's answer but with getter instead of method/function when applicable -- mirroring Dart's computed properties like String.isEmpty.
Coming from C#, I like their String's helper method IsNullOrWhiteSpace.
My version merges the above two concepts.
Naming
Rename the Extension classes whatever you like. I tend to name them like this:
XYExtension
Where:
X is File/Author/App/Unique name.
Y is name of Type being extended.
Name examples:
MyAppIntExtension
DartDoubleExtension
TextUtilsStringExtension
OHProviderExtension
Criticism
Why private getters instead of simple
this == null || this == '' || this == [] || this == 0 || !this
I think this is safer as it first casts the object to the right type we want to compare its value to.
More modular as changes are central within the private getters and any modification is reflected everywhere.
If type check fails within the private getter, we return false which doesn't affect the outcome of the root logical-OR expression.
I would write a helper function instead of doing everything inline.
bool isNullEmptyOrFalse(Object o) =>
o == null || false == o || "" == o;
bool isNullEmptyFalseOrZero(Object o) =>
o == null || false == o || 0 == o || "" == o;
That avoids the repeated lookup (like the contains operation), but it is much more readable. It also doesn't create a new List literal for each check (making the list const could fix that).
if (isNullEmptyOrFalse(routeinfo["no_route"])) { ... }
When struggling with making something short and readable, making a well-named helper function is usually the best solution.
(Addition: Now that Dart has extension methods, it's possible to add the functionality as methods or getters that are seemingly on the object, so you can write value.isNullOrEmpty directly).
As coming from Android and Kotlin, I prefer extension methods over a static helper method. And with Dart 2.7 you can now use that as well:
extension Extension on Object {
bool isNullOrEmpty() => this == null || this == '';
bool isNullEmptyOrFalse() => this == null || this == '' || !this;
bool isNullEmptyZeroOrFalse() =>
this == null || this == '' || !this || this == 0;
}
Now you can just call these methods from anywhere in your code:
if (anyVariable.isNullOrEmpty()) {
// do something here
}
You might need to manually import the dart class, where you put your extension methods, for example:
import 'package:sampleproject/utils/extensions.dart';
With Null safety:
Say, you have a nullable Map and a List which have nullable values in it.
Map<String, List?>? map;
List<String?>? list;
To check if the collection is neither null nor empty, you can do:
if (map?.containsKey('foo') ?? false) {
print('map is not-null, and key "foo" is present.');
}
if (list?.isNotEmpty ?? false) {
print('list is not-null and not empty');
}
For strings i like this approach:
extension NullableStringExtensions<E> on String? {
/// Returns `true` if this string is `null` or empty.
bool get isNullOrEmpty {
return this?.isEmpty ?? true;
}
/// Returns `true` if this string is not `null` and not empty.
bool get isNotNullNorEmpty {
return this?.isNotEmpty ?? false;
}
}
Credits go to the author of this package: https://pub.dev/packages/string_ext
bool isNullString(String? value) {
if (value == null || value.isEmpty ) {
return true;
} else {
return false;
}
}
and use this method like
isNullString(yourValue)
package:quiver has an isEmpty function that returns true if the argument is null or the empty string.
It's also trivial to implement such a function yourself.
function isEmpty(obj) {
return isNone(obj) || (obj.length === 0 && typeof obj !== 'function') || (typeof obj === 'object' && get(obj, 'length') === 0);
}
1) For null and undefined, we have isNone() function
2) For [], '', we have the second check.
3) The purpose of third check?
I believe that is to check for empty objects that have length as an Ember computed property. IE8 and below has no support for Javascript computed properties, so you have to use the Ember syntax to compute them.
Regarding the new Ember.js routing system (described here), if I understand correctly, views are destroyed when you exit a route.
Is there any way to bypass destruction of views upon exiting a route, so that the state of the view is preserved when the user re-enters the route?
Update: Looks like, views are not destroyed unless the outlet view is being replaced in the new route. For e.g., if you are in stateA with ViewA in some {{outlet master}} and you go to stateB with ViewB in {{outlet master}}, then ViewB will replace ViewA. A way around this is to define multiple outlets when you need to preserve views, e.g., {{outlet master1}}, {{outlet master2}}, ...
A nice feature would be the ability to pass an array of views to the outlet. And also be able to choose whether views will be destroyed or just become hidden, upon exiting a route.
I have since figure out how to modify the routing system, so that views inserted into outlets are not destroyed. First I override the Handlebars outlet helper, so that it loads an Ember.OutletView into {{outlet}}:
Ember.Handlebars.registerHelper('outlet', function(property, options) {
if (property && property.data && property.data.isRenderData) {
options = property;
property = 'view';
}
options.hash.currentViewBinding = "controller." + property;
return Ember.Handlebars.helpers.view.call(this, Ember.OutletView, options);
});
Where Ember.OutletView extends Ember.ContainerView as follows:
Ember.OutletView = Ember.ContainerView.extend({
childViews: [],
_currentViewWillChange: Ember.beforeObserver( function() {
var childViews = this.get('childViews');
// Instead of removing currentView, just hide all childViews
childViews.setEach('isVisible', false);
}, 'currentView'),
_currentViewDidChange: Ember.observer( function() {
var childViews = this.get('childViews'),
currentView = this.get('currentView');
if (currentView) {
// Check if currentView is already within childViews array
// TODO: test
var alreadyPresent = childViews.find( function(child) {
if (Ember.View.isEqual(currentView, child, [])) {
return true;
}
});
if (!!alreadyPresent) {
alreadyPresent.set('isVisible', true);
} else {
childViews.pushObject(currentView);
}
}
}, 'currentView')
});
Basically we override _currentViewWillChange() and just hide all childViews instead of removing the currentView. Then in _currentViewDidChange() we check if the currentView is already inside childViews and act accordingly. The Ember.View.isEqual is a modified version of Underscore isEqual:
Ember.View.reopenClass({
isEqual: function(a, b, stack) {
// Identical objects are equal. `0 === -0`, but they aren't identical.
// See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.
if (a === b) return a !== 0 || 1 / a == 1 / b;
// A strict comparison is necessary because `null == undefined`.
if (a == null || b == null) return a === b;
// Unwrap any wrapped objects.
if (a._chain) a = a._wrapped;
if (b._chain) b = b._wrapped;
// Compare `[[Class]]` names.
var className = toString.call(a);
if (className != toString.call(b)) return false;
if (typeof a != 'object' || typeof b != 'object') return false;
// Assume equality for cyclic structures. The algorithm for detecting cyclic
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
var length = stack.length;
while (length--) {
// Linear search. Performance is inversely proportional to the number of
// unique nested structures.
if (stack[length] == a) return true;
}
// Add the first object to the stack of traversed objects.
stack.push(a);
var size = 0, result = true;
// Recursively compare objects and arrays.
if (className == '[object Array]') {
// Compare array lengths to determine if a deep comparison is necessary.
size = a.length;
result = size == b.length;
if (result) {
// Deep compare the contents, ignoring non-numeric properties.
while (size--) {
// Ensure commutative equality for sparse arrays.
if (!(result = size in a == size in b && this.isEqual(a[size], b[size], stack))) break;
}
}
} else {
// Objects with different constructors are not equivalent.
if (a.get('constructor').toString() != b.get('constructor').toString()) {
return false;
}
// Deep compare objects.
for (var key in a) {
if (a.hasOwnProperty(key)) {
// Count the expected number of properties.
size++;
// Deep compare each member.
if ( !(result = b.hasOwnProperty(key) )) break;
}
}
}
// Remove the first object from the stack of traversed objects.
stack.pop();
return result;
}
});
So that the state of the view is preserved when the user re-enters the
route.
I would, instead, store that information in the controller (or the state manager) so that when the route is re-entered, the new view is initialized with the old state. Does that make sense? So, for example, if it's a list of posts, and one is selected, you would store the data about which post was selected in the controller (or the state manager). After visiting a specific post and then coming back to the list, that same post would be selected.
I can imagine a use case where this wouldn't be very useful (e.g. scrolling to a specific position in a long list) so maybe that doesn't answer your question.