Search a struct nested in an array - arrays

I have a struct that's inside an array that is nested inside another struct, like so: Arguments.cart.data.Items[x].Labels.Pkg.Title (x is an index, as I'm looping over Items).
Items is an array while Labels, Pkg, and Title are nested structs.
Title does not always exist. So I would like to check for it. However, using structFindKey returns an error
You have attempted to dereference a scalar variable of type class coldfusion.runtime.Array as a structure with members
I could just look inside Arguments.cart.data; however, if there are multiple rows in the array, some rows may contain Title while others do not. So I want to check for Title inside each Items.
I've also tried arrayFind, but then I get the error
Struct cannot be used as an array
I'm at a loss here.

This will do the job
<cfscript>
for (i=1;i<=ArrayLen(arguments.cart.data.Items);i++) {
tempI = arguments.cart.data.Items[i];
if (IsDefined('tempI.Labels.Pkg.Title')) {
// It exists
} else {
// It doesn't
}
}
</cfscript>
IsDefined doesn't play nicely with arrays, but by assigning each element of the array to the temp value you are then able to refer to it within IsDefined.
Alternatively you can do the following, if you prefer StructKeyExists
<cfscript>
for (i=1;i<=ArrayLen(arguments.cart.data.Items);i++) {
tempI = arguments.cart.data.Items[i];
if (
StructKeyExists(tempI,'Labels')
&& StructKeyExists(tempI.Labels,'Pkg')
&& StructKeyExists(tempI.Labels.Pkg,'Title')
) {
// It exists
} else {
// It doesn't
}
}
</cfscript>

I've come across this in the past too. Just stick your array into a struct temporarily... this will trick structFindKey() and structFindValue() into working properly.

Related

C# - Tuple arrays are mutable, but tuple lists are not. How do I get around this?

I have an array of value pairs I want to modify. I need to add and remove values from this array as well, so I used a list. When I tried to use a list, I encountered an error.
Error CS1612 - Cannot modify the return value of 'List<(int, float)>.this[int]' because it is not a variable
So I decided I would investigate. I tried using an array instead, and it... worked fine? The following code only throws an error on arr1[0].Item1 += 1;.
static void Main()
{
List<(int, float)> arr1 = new List<(int, float)>() { (0, 0) };
(int, float)[] arr2 = new (int, float)[1];
arr1[0].Item1 += 1; // This line
arr2[0].Item1 += 1;
}
Why are tuple arrays mutable, but lists are not? Is this because arrays are simple blocks of data you can modify easily, but lists have a lot of backend behind them that complicates things? Is there a simple way to get around this, or am I going to have to make my own custom class?
Why are tuple arrays mutable, but lists are not?
The list itself is mutable, but not in the way you're doing it. Note that this isn't anything specific to tuples - it's just the case for any mutable struct.
The list indexer getter returns a value (i.e. a copy of the tuple in your case) - so modifying that value wouldn't modify the copy in the list. The compiler is trying to avoid you making a change to a value that's about to be thrown away. Array access doesn't do that - arr2[0] refers to the variable within the array. (An array is effectively a collection of variables.)
If you want to mutate the list, you can have to fetch the tuple, mutate it, then put it back:
var tuple = arr1[0];
tuple.Item1++;
arr1[0] = tuple;
Note that this also explains why you can't use list access expressions as arguments for ref parameters, but you can do the equivalent for arrays:
public void Method(ref int x) => x++;
public void CallMethod()
{
var list = new List<int> { 0 };
var array = new int[] { 0 };
Method(ref list[0]); // Error
Method(ref array[0]); // Valid
}

Loop over array and return TRUE if value exists

I would like to iterate over an array and if a value exists, I would like to return TRUE.
struct Loops {
var loopStep: LoopStep
}
struct LoopStep {
var template: [Template]
}
struct Template {
var stepType: String
}
let templates: [Template] = [Template(stepType: "FORM_ONE"), Template(stepType: "FORM_TWO")]
let loopStep = LoopStep(template: templates)
let incompleteSteps = [Loops(loopStep: loopStep)]
I have tried this using reduce however cannot make this syntax work
let result = incompleteSteps.reduce(true, $0.loopStep.template.stepType == "FORM_ONE" )
You simply need to use contains(where:) to get a bool return value indicating whether an element matching a closure exists in the collection or not. Since template is an Array itself as well, you actually need to nest two contains(where:) calls if you want to find out if an array of Loops contains any Loops whose template array contains a Template with the matching requirement.
let result = incompleteSteps.contains(where: {$0.loopStep.template.contains(where: {$0.stepType == "FORM_ONE"})})

Getting the indexes of NSButtons in an array and appending them to an array of integers in swift (Cocoa)

I'm super new to programming so this may be a pretty basic question.
I have an array of NSButtons (checkboxes) in my ViewController that I am calling "buttonArray." I want to go through the array, find out which buttons are checked and add the indexes of those checked buttons to another array of integers (located in a struct), which I am calling "intArray." This is the struct:
struct GetInterval {
var intArray = [Int]()
mutating func putIntIntoArray (intToAdd:Int) {
self.intArray.append(intToAdd)
}
}
I have tried doing this two ways (shown below), both of which have given me compiler errors.
First, I tried to use a function to import the index as an Int and add it to the "intArray" array...
for interval in buttonArray {
if interval.state == NSOnState {
var intervalIndex = find(buttonArray, interval)
GetInterval.putIntIntoArray(Int(intervalIndex!))
}
}
...which gave me the error: "Cannot invoke 'putIntoArray' with argument list of type ((int))"
When that didn't work, I tried appending it directly from the "if" statement in the ViewController...
for interval in buttonArray {
if interval.state == NSOnState {
var intervalIndex = find(buttonArray, interval)
GetInterval.intArray.append(intervalIndex!)
}
}
...which gives me the error: "GetInterval.Type does not have a member named 'intArray'"
How can I fix this?
When you say GetInterval.(something), this refers to a static member (a.k.a., something owned by the GetInterval type). However, your intArray is a member owned by each instance of the GetInterval type. So you need to create an instance first. For example:
var getInt = GetInterval() // create a new instance
getInt.putIntIntoArray(...) // modify this instance, by calling a mutating function
getInt.intArray.append(...) // modify this instance, by directly accessing a property

Swift - issue with initializing and appending to arrays in dictionary

Here's what I am trying to do:
I am starting with an array of ArticleItem objects. Those objects have a property on them named 'category' which is a string. I'm trying to loop through all of my ArticleItem objects and group items with like categories in a dictionary. I'm using the category name as my key. The issue I am having is that my dictionary keys are hold arrays that never contain more than 1 object. I definitely have more than 3 objects with the same category name. Here is the relevant code from my class. I'd love to understand the right way to do this..
private var _articlesDict:[String:[ArticleItem]]
init(articles:[ArticleItem]) {
_articlesDict = [String:[ArticleItem]]()
for item:ArticleItem in articles {
var optionalCatArray:[ArticleItem]? = _articlesDict[item.category]
if let catArray = optionalCatArray {
optionalCatArray!.append(item) //why can't I do catArray.append(item)?
} else {
var arr:[ArticleItem] = [ArticleItem]()
arr.append(item)
_articlesDict[item.category] = arr
}
}
}
The problem is that arrays are value types, so they are passed by value and not by reference. That means that every time you assign a variable holding an array to another variable (or array, or dictionary) you actually create a copy of it. But there's more.
1st problem
This line of code:
if let catArray = optionalCatArray {
creates an immutable copy of optionalCatArray, so it cannot be modified. Use this instead:
if optionalCatArray != nil {
2nd problem
This line of code:
var optionalCatArray:[ArticleItem]? = _articlesDict[item.category]
creates a copy of the array stored in the dictionary - here:
if optionalCatArray != nil {
optionalCatArray!.append(item)
assign a new item to the array, but remember: this is a copy, so you are not modifying the array contained in the dictionary. What's missing is setting it back into the dictionary:
if optionalCatArray != nil {
optionalCatArray!.append(item)
_articlesDict[item.category] = optionalCatArray!
}
Probably this code can be improved by avoiding the array copy like this:
if _articlesDict[item.category] != nil {
_articlesDict[item.category]!.append(item)
} else {
_articlesDict[item.category] = [item]
}
I haven't tested it, but conceptually it should work. Also note how I shortened the else branch, easier to read.
You cannot edit an array in a dictionary directly. You append to a local copy only.
See https://stackoverflow.com/a/24251066/1183577 for more info.
As an alternative to the native-array solutions in other answers, you could do this:
var _articlesDict = [String:NSMutableArray]()
init(articles:[ArticleItem]) {
for item:ArticleItem in articles {
if let array = _articlesDict[item.category] {
array.addObject(item)
} else {
_articlesDict[item.category] = NSMutableArray(object:item)
}
}
}
But keep in mind you'll have to cast to ArticleItem when you extract items from the arrays in the dictionary.

List.forEach unable to modify element?

I'm attempting to create a list of lists to imitate the functionality of a 2D array in Dart, and I was having trouble for a while figuring out how to make it work.
I originally used List.forEach in order to create the lists and fill them, but after each loop, it's as if I never created the lists. Here's what that code looked like:
currentMap = new List<List<int>>(height);
currentMap.forEach((e) {
e = new List<int>(width);
e.forEach((f) {
f = 1;
});
});
Printing currentMap resulted in a list of null. Here's what I have now:
currentMap = new List<List<int>>(height);
for(int i = 0; i < height; i++) {
currentMap[i] = new List<int>(width);
for(int j = 0; j < width; j++) {
currentMap[i][j] = 1;
}
}
This works exactly as I expected it to.
I understand that this is probably a very basic issue, and I am assuming that forEach does not allow you to modify the element, but I wanted some confirmation on that, and the Dart API docs do not specify except with the phrase "apples the function f to each element..." which may just not be clear to me.
Also, I am aware of things like the List.filled() constructor -- this is just how I am starting to build towards other functionality.
EDIT: Okay, I think I understand now. Arguments are "pass-by-sharing" which means that a copy of a reference to the object is made. This means that you can modify a member of a mutable object that is pointed to by the argument (as follows):
void function(MyObject o) {
o.x = 5;
}
but trying to change what o points to will not change the argument after exiting the function, such as this:
void function(MyObject o) {
MyObject p = new MyObject();
o = p;
}
Dart does not have variable references, all elements are passed as a reference to an object, not to a variable. (In other words, Dart is purely "call-by-sharing" like both Java and JavaScript).
That means that the e parameter to the forEach callback is just a normal local variable, and assigning to it has no effect outside the callback. The same goes for iterators: they return the value in the iterable, but it has no reference back to the iterable after that.
The double loop is what you want when you do "read-modify-write". You read a value from the list, modify it and store it back into the list using the list's index-set operator.
I know you know about List.filled, but just for other readers, I'll give the one-liner to create the two-dimensional array initialized to 1 values:
currentMap = new List.generate(height, (_) => new List.filled(width, 1));
This is because e is only a reference to the List in currentMap not to a currentMap item.
You update a copy of the reference to a List, but this reference has no connection to currentMap.
After the loop, e just gets garbage collected.
This is because of how an Enumerator works and I believe because the Enumerator is immutable You can't modify the collection items when it's being processed in a foreach... removing members and such.
See also:
http://msdn.microsoft.com/en-us/library/ttw7t8t6.aspx
What is the best way to modify a list in a 'foreach' loop?

Resources