Dealing with nested for loops in Swift for a JSON file - json

One of the JSON requests that I make returns a file with a bunch of nested information. The format is roughly as follows groups->individual group->teams in group.
Currently I am dealing with this with a nested for loop where I look at the outer groups and then run the inner loop to get the information for the individual teams.
I've uploaded a copy of the JSON file to paste bin, here is the link. http://pastebin.com/D14wYDEs. This particular example doesn't have that many groups and teams but its possible to have way more, which makes the concept of nested for loops seem impractical.
I was wondering if somebody had a suggestion as to a better system of doing this, or any suggestion really.
Heres my current code:
func generateTablaDePosiciones() {
estadisticaUtilizada = 3
var tablaDePosicionesJSON = getJSONStats(3,tkn,eqID)
//checks to see that contents != nil, meaning the JSON file was found
if tablaDePosicionesJSON != nil {
tablaDePosicionesArray.removeAll(keepCapacity: false)
var numeroDeGruposEnTablaDePosiciones = tablaDePosicionesJSON["grupos"].count
for var index = 0; index < numeroDeGruposEnTablaDePosiciones; ++index {
var grupo = tablaDePosicionesJSON["grupos"][index]["grupo"].string
var etiqueta1 = tablaDePosicionesJSON["grupos"][index]["etiqueta-1"].string
var etiqueta2 = tablaDePosicionesJSON["grupos"][index]["etiqueta-2"].string
var etiqueta3 = tablaDePosicionesJSON["grupos"][index]["etiqueta-3"].string
var etiqueta4 = tablaDePosicionesJSON["grupos"][index]["etiqueta-4"].string
var etiqueta5 = tablaDePosicionesJSON["grupos"][index]["etiqueta-5"].string
var preTablaDePosicionesNuevo = preTablaDePosiciones(grupo: grupo!, etiqueta1: etiqueta1!, etiqueta2: etiqueta2!, etiqueta3: etiqueta3!, etiqueta4: etiqueta4!, etiqueta5: etiqueta5!)
preTablaDePosicionesArray.append(preTablaDePosicionesNuevo)
numeroDeTablaDePosiciones = tablaDePosicionesJSON["grupos"][index]["lista-body"].count
for(var innerIndex = 0; innerIndex < numeroDeTablaDePosiciones; ++innerIndex) {
var rank = tablaDePosicionesJSON["grupos"][index]["lista-body"][innerIndex]["rank"].string
var equipoID = tablaDePosicionesJSON["grupos"][index]["lista-body"][innerIndex]["equipoID"].number! as Int
var nomEquipo = tablaDePosicionesJSON["grupos"][index]["lista-body"][innerIndex]["nom-equipo"].string
var d1 = tablaDePosicionesJSON["grupos"][index]["lista-body"][innerIndex]["d1"].string
var d2 = tablaDePosicionesJSON["grupos"][index]["lista-body"][innerIndex]["d2"].string
var d3 = tablaDePosicionesJSON["grupos"][index]["lista-body"][innerIndex]["d3"].string
var d4 = tablaDePosicionesJSON["grupos"][index]["lista-body"][innerIndex]["d4"].string
var d5 = tablaDePosicionesJSON["grupos"][index]["lista-body"][innerIndex]["d5"].string
var tablaDePosicionesNuevo = tablaDePosiciones(rank: rank!, equipoID: equipoID, nomEquipo: nomEquipo!, d1: d1!, d2: d2!, d3: d3!, d4: d4!, d5: d5!)
tablaDePosicionesArray.append(tablaDePosicionesNuevo)
}
}
} else {
estadisticaUtilizada = 0
println("Tabla de Posiciones JSON was nil")
}
}

I would use a while loop. Increment an index with each execution and then dynamically construct the key using that index. Collect your results in an array and then pass on that array instead of each individual object.
Also, you should really be unwrapping all these values as you're parsing them instead of force unwrapping (!). In this while loop you can use a conditional binding while let to handle that, and if it fails—i.e. it found no value for that key—it will exit.
Something like this:
var index = 1
var results = [String]()
while let etiqueta = grupo["etiqueta-\(index)"] as? String {
results.append( etiqueta )
index++
}
let preTablaDePosicionesNuevo = preTablaDePosiciones(grupo: name, etiquetas: results)
preTablaDePosicionesArray.append( preTablaDePosicionesNuevo )

Related

Can we recall a set of variable inside the Sequence Array?

I'd like to ask about my program bcs it doesn't work correctly. I want to recall a set of variable in two different Sequence Array. Here is my code.
// Array of Arrays
var SequenceGo:Array =
\[
{dt:dt1, P:P1, s0:s01, s:s1},
{dt:dt2, P:P2, s0:s02, s:s2},
{dt:dt3, P:P3, s0:s03, s:s3},
{dt:dt4, P:P4, s0:s04, s:s4},
{dt:dt5, P:P5, s0:s05, s:s5},
{dt:dt6, P:P6, s0:s06, s:s6},
{dt:dt7, P:P7, s0:s07, s:s7},
{dt:dt8, P:P8, s0:s08, s:s8},
{dt:dt9, P:P9, s0:s09, s:s9},
{dt:dt10, P:P10, s0:s010, s:s10},
\];
var SequenceBack:Array =
\[
{dtback:dt10back, P:P10, s0:s010, sback:s10back},
{dtback:dt9back, P:P9, s0:s09, sback:s9back},
{dtback:dt8back, P:P8, s0:s08, sback:s8back},
{dtback:dt7back, P:P7, s0:s07, sback:s7back},
{dtback:dt6back, P:P6, s0:s06, sback:s6back},
{dtback:dt5back, P:P5, s0:s05, sback:s5back},
{dtback:dt4back, P:P4, s0:s04, sback:s4back},
{dtback:dt3back, P:P3, s0:s03, sback:s3back},
{dtback:dt2back, P:P2, s0:s02, sback:s2back},
{dtback:dt1back, P:P1, s0:s01, sback:s1back}
\];
function onNext(index:int = 0):void
{
if (index >= SequenceGo.length)
{
return;
}
var aDataGo:Object = SequenceGo[index];
var aDataBack:Object = SequenceBack[index];
//variables
F = s_teganganst.value;
m = s_masjenst.value/10000;
v = Math.sqrt(F/m);
tp = 5000/v;
f = s_frekuensist.value;
w = 2*Math.PI*f;
aDataGo.dt += t;
aDataGo.s = aDataGo.s0 - A * Math.sin(w * aDataGo.dt);
aDataGo.P.y = aDataGo.s;
if(P10.y < 607){
aDataBack.dtback += t;
aDataBack.sback = - A * Math.sin(w * aDataBack.dtBack);
aDataBack.P.y = aDataGo.s + aDataBack.sback;
}
setTimeout(onNext, tp, index + 1);
}
Actually, code
aDataBack.P.y = aDataGo.s + aDataBack.sback;
is not a fit code for the animation because aDataBack is ordered inversely from aDataGo (we have to stay this inverse order for the proper animation in my program). I want to recall the variables based on its number, so each variable will match with another variable. For example,
P1.y = s1 + s1back;
P2.y = s2 + s2back;
P3.y = s3 + s3back;
P4.y = s4 + s4back;
//and so on
I've tried the code above, but it also doesn't work. Any other expression for calling some couples of variables just like my code above? Thanks!
I want to recall the variables based on its number, so each variable will match with another variable
Ok, there are two options.
Option one, simple and straightforward: compose a method to find the correspondent back object on spot:
function findBack(P:Object):Object
{
for each (var aDataBack:Object in SequenceBack)
{
if (aDataBack.P == P)
{
return aDataBack;
}
}
}
So, that piece of code would be
var aDataGo:Object = SequenceGo[index];
var aDataBack:Object = findBack(aDataGo.P);
The possible problem here is the performance. It is fine on the scale of 10 or 100 objects, but as (I suppose) you devise a particle system, the object count easily scales to thousands, and the amount of loop-searching might become cumbersome.
So I advise to prepare a pre-indexed hash so that you won't need to search each single time.
var SequenceBack:Array =
[
// ...
];
// Dictionary is a storage of key:value data, just like Object,
// but Dictionary allows Object keys.
var HashBack:Dictionary = new Dictionary;
for each (var aDataBack:Object in SequenceBack)
{
HashBack[aDataBack.P] = aDataBack;
}
I encourage you to read more about Dictionary class.
And so that piece of code would be
var aDataGo:Object = SequenceGo[index];
var aDataBack:Object = HashBack[aDataGo.P];

Kotlin - How to make a for loop that iterate and return multiple values

I created a function that iterates by a if statement over a list in order to find a match, when found I wanted to return the match value, but it only happen once, the return statements are at the end of the function and the if statement.
The question is, How can I avoid that this function stops after the first match?, is there another way?, other functions that im not using?
When i run this code I get this:
Anything
Not a match
Not a match
Here is my code:
class Class1(var self: String,var tipo: String,var element: String)
var test_class = Class1("","","")
fun giver(){
test_class.self = "Anything"
test_class.tipo = "Something"
test_class.element = "Nothing"
}
class Funciones(){
fun match_finder(texto: String): Any{
var lista = listOf<String>(test_class.self,test_class.tipo,test_class.element)
var lista_de_listas = listOf<String>("test_class.self","test_class.tipo","test_class.element")
var count = -1
var variable = ""
for (i in lista_de_listas){
count = count + 1
println(count)
if (texto == i){
lista_de_listas = lista
var variable = lista_de_listas[count]
return variable
}
}
return "Not a match"
}
}
fun main(){
giver()
var x = "test_class.self"
var z = "test.class.tipo"
var t = "test.class.element"
var funcion = Funciones()
var y = funcion.match_finder(x)
var c = funcion.match_finder(z)
var r = funcion.match_finder(t)
println(y)
println(c)
println(r)
}
You have some typos in your example. You query for test.class.tipo but in your lista_de_listas you have test_class.tipo with underline. The same is true for test.class.element.
But you should consider to use a Map instead of two list to the lookup:
fun match_finder(texto: String): Any{
val map = mapOf(
"test_class.self" to test_class.self,
"test_class.tipo" to test_class.tipo,
"test_class.element" to test_class.element
)
return map.getOrDefault(texto,"Not a match")
}

How to get nested deep property value from JSON where key is in a variable?

I want to bind my ng-model with JSON object nested key where my key is in a variable.
var data = {"course":{"sections":{"chapter_index":5}}};
var key = "course['sections']['chapter_index']"
Here I want to get value 5 from data JSON object.
I found the solution to convert "course.sections.chapter_index" to array notation like course['sections']['chapter_index'] this. but don't know how to extract value from data now
<script type="text/javascript">
var BRACKET_REGEXP = /^(.*)((?:\s*\[\s*\d+\s*\]\s*)|(?:\s*\[\s*"(?:[^"\\]|\\.)*"\s*\]\s*)|(?:\s*\[\s*'(?:[^'\\]|\\.)*'\s*\]\s*))(.*)$/;
var APOS_REGEXP = /'/g;
var DOT_REGEXP = /\./g;
var FUNC_REGEXP = /(\([^)]*\))?$/;
var preEval = function (path) {
var m = BRACKET_REGEXP.exec(path);
if (m) {
return (m[1] ? preEval(m[1]) : m[1]) + m[2] + (m[3] ? preEval(m[3]) : m[3]);
} else {
path = path.replace(APOS_REGEXP, '\\\'');
var parts = path.split(DOT_REGEXP);
var preparsed = [parts.shift()]; // first item must be var notation, thus skip
angular.forEach(parts, function (part) {
preparsed.push(part.replace(FUNC_REGEXP, '\']$1'));
});
return preparsed.join('[\'');
}
};
var data = {"course":{"sections":{"chapter_index":5}}};
var obj = preEval('course.sections.chapter_index');
console.log(obj);
</script>
Hope this also help others. I am near to close the solution,but don't know how can I get nested value from JSON.
This may be a good solution too
getDeepnestedValue(object: any, keys: string[]) {
keys.forEach((key: string) => {
object = object[key];
});
return object;
}
var jsonObject = {"address": {"line": {"line1": "","line2": ""}}};
var modelName = "address.line.line1";
var result = getDescendantPropValue(jsonObject, modelName);
function getDescendantPropValue(obj, modelName) {
console.log("modelName " + modelName);
var arr = modelName.split(".");
var val = obj;
for (var i = 0; i < arr.length; i++) {
val = val[arr[i]];
}
console.log("Val values final : " + JSON.stringify(val));
return val;
}
You are trying to combine 'dot notation' and 'bracket notation' to access properties in an object, which is generally not a good idea.
Source: "The Secret Life of Objects"
Here is an alternative.
var stringInput = 'course.sections.chapter_index'
var splitInput = stringInput.split(".")
data[splitInput[1]]][splitInput[2]][splitInput[3]] //5
//OR: Note that if you can construct the right string, you can also do this:
eval("data[splitInput[1]]][splitInput[2]][splitInput[3]]")
Essentially, if you use eval on a string, it'll evaluate a statement.
Now you just need to create the right string! You could use the above method, or tweak your current implementation and simply go
eval("data.course.sections.chapter_index") //5
Source MDN Eval docs.
var data = {
"course": {
"sections": {
"chapter_index": 5
}
}
};
var key = "course['sections']['chapter_index']";
var keys = key.replace(/'|]/g, '').split('[');
for (var i = 0; i < keys.length; i++) {
data = data[keys[i]];
}
console.log(data);
The simplest possible solution that will do what you want:
var data = {"course":{"sections":{"chapter_index":5}}};
var key = "course['sections']['chapter_index']";
with (data) {
var value = eval(key);
}
console.log(value);
//=> 5
Note that you should make sure key comes from a trusted source since it is eval'd.
Using with or eval is considered dangerous, and for a good reason, but this may be one of a few its legitimate use cases.
If you don't want to use eval you can do a one liner reduce:
var data = {"course":{"sections":{"chapter_index":5}}};
var key = "course['sections']['chapter_index']"
key.split(/"|'|\]|\.|\[/).reduce((s,c)=>c===""?s:s&&s[c], data)

Reverse engineering - Flash app

I have that code:
private function handleFlashVarsXmlLoaded(event:Event) : void
{
var secondsplit:String = null;
var item:Array = null;
var string:* = XML(String(event.target.data));
var notsplited:* = string.vars_CDATA; //what is .vars_CDATA?
var splitted:* = notsplitted.split("&");
var datacontainer:Object = {};
var index:Number = 0;
item = secondsplit.split("=");
datacontainer[item[0]] = item[1];
this.parseFlashVars(datacontainer); // go next
return;
}
That function is loaded when URLLoader is loaded.
I think that this function parse a XML file to string(fe. param1=arg1&param2=arg2), then split it by "&" and then by "=" and add data to datacontainer by
datacontainer["param1"] = "arg1"
But how should the XML file look like and what is string.vars_CDATA
I think, vars_CDATA is just a name of XML field, becourse variable named "string" is contains whole XML. So var "notsplited" contains a String-typed data of this field (I think so, becourse of the line "var splitted:* = notsplitted.split("&");", which splits String to Array).

actionscript arrays merge

I posted my problem a few hours ago, but I think I figured out how to ask my question in a more comprehensible way.
This is my code:
// 1. Intro
var introPL1:Array = ["intro1","intro2","intro3","intro4"];
var introPL2:Array = ["intro5","intro6","intro7","intro8","intro9"];
var introPL3:Array = ["intro10","intro11"];
var introPL4:Array = ["intro12","intro13"];
var allIntro:Array = [introPL1,introPL2,introPL3,introPL4];
// 2. Clothes
var clothesPL1:Array = ["clothes1","clothes2","clothes3","clothes4","clothes5"];
var clothesPL2:Array = ["clothes6","clothes7","clothes8"];
var clothesPL3:Array = ["clothes9","clothes10"];
var clothesPL4:Array = ["clothes11","clothes12","clothes13"];
var allClothes:Array = [clothesPL1,clothesPL2,clothesPL3,clothesPL4];
// 3. Colored Numbers
var colNumPL1:Array = ["colNum1","colNum2","colNum3","colNum4","colNum5"];
var colNumPL2:Array = ["colNum6","colNum7","colNum8"];
var colNumPL3:Array = ["colNum9","colNum10"];
var colNumPL4:Array = ["colNum11","colNum12","colNum13"];
var allColNum:Array = [colNumPL1,colNumPL2,colNumPL3,colNumPL4];
var allStuff:Array;
allStuff = allIntro.concat(allClothes, allColNum);
trace(allStuff[4]);
When I trace allStuff[4] it displays "clothes1,clothes2,clothes3,clothes4,clothes5".
The thing is, I would like all the stuff to be in the allStuff array (without sub-arrays) and when I trace allStuff[4], I would like it to display "intro5" (the fifth item in the huge allStuff array).
the function you want to use then is concat
here's the example from adobe
var numbers:Array = new Array(1, 2, 3);
var letters:Array = new Array("a", "b", "c");
var numbersAndLetters:Array = numbers.concat(letters);
var lettersAndNumbers:Array = letters.concat(numbers);
trace(numbers); // 1,2,3
trace(letters); // a,b,c
trace(numbersAndLetters); // 1,2,3,a,b,c
trace(lettersAndNumbers); // a,b,c,1,2,3
it's pretty straight forward:
allStuff= allStuff.concat(introPL1,introPL2,introPL3,introPL4,clothesPL1,clothesPL2,clothesPL3,clothesPL4,colNumPL1,colNumPL2,colNumPL3,colNumPL4);
you could also do a
allStuff = []
for each(var $string:String in $arr){
allStuff.push($string)
}
for each array, or make it into a function
Okay, once you have declared your arrays like so, you need an additional operation to flatten your arrays allClothes and so on. Do like this:
function flatten(a:Array):Array {
// returns an array that contains all the elements
// of parameter as a single array
var b:Array=[];
for (var i:int=0;i<a.length;i++) {
if (a[i] is Array) b=b.concat(flatten(a[i]));
else b.push(a[i]);
}
return b;
}
What does it do: The function makes an empty array first, then checks the parameter member by member, if the i'th member is an Array, it calls itself with that member as a parameter, and adds the result to its temporary array, otherwise it's just pushing next member of a into the temporary array. So, to make your allIntro a flat array, you call allIntro=flatten(allIntro) after declaring it as you did. The same for other arrays.