Immutable.js and shortcut for getting at a key entry - immutable.js

So, I am using Immutable.js and had a normal Immutable.Map and had to switch up the object a little because it kept sorting the object when I didn't want it to (previously, I was using a hash, now as you see, an array). Even an OrderedMap didn't work, so I put the "new" object like so, and now of course, the obj retains its ordering. BUT, now I have to iterate thru it every time I want to get a specific ID. Seems wasteful, I was curious if there is a helper function in which I can just request a key (id), in this case, and get back the appropriate obj.
"sneakers": Immutable.List([
[{_id: 1, color: "red", price: 250}],
[{_id: 1638, color: 728, price: 90}]
etc...
so, if I wanted the obj in which the _id is 1638, I'd have to filter thru it. Previously I could just "getIn". Is there a quick way with Immutable.js given this data structure?

This is not the perfect solution, but if you have always one object inside inner array and all _id's are unique, you can use:
sneakers.find(function(data) {
return data.find(function(innerArr) {
return innerArr.get("_id") === 1638
})
}).get(0).toJS();
In this solution, you do not need to iterate all List, it returns when it finds first occurrence.
A small reminder: Immutable.List converts your list to immutable only for one level, inner array is still mutable. You should use fromJS() instead of List. My solution works for fromJS() usage.

Related

Convert List<dynamic> to List<String>

I am getting data from server. The run runtimeType shows that they have type List.
Currently I am using cast<String>() to get List<String>.
But is it's only\right way?
var value = await http.get('http://127.0.0.1:5001/regions');
if(value.statusCode == 200) {
return jsonDecode(value.body)['data'].cast<String>();
}
There are multiple ways, depending on how soon you want an error if the list contains a non-string, and how you're going to use the list.
list.cast<String>() creates a lazy wrapper around the original list. It checks on each read that the value is actually a String. If you plan to read often, all that type checking might be expensive, and if you want an early error if the last element of the list is not a string, it won't do that for you.
List<String>.from(list) creates a new list of String and copies each element from list into the new list, checking along the way that it's actually a String. This approach errs early if a value isn't actually a string. After creation, there are no further type checks. On the other hand, creating a new list costs extra memory.
[for (var s in list) s as String],
[... list.cast<String>()],
<String>[for (var s in list) s],
<String>[... list] are all other ways to create a new list of strings. The last two relies on implicit downcast from dynamic, the first two uses explicit casts.
I recommend using list literals where possible. Here, I'd probably go for the smallest version <String>[...list], if you want a new list. Otherwise .cast<String>() is fine.

Setting value using UseState hook adds a "tableData" object to data fetched from API

I am fetching data from a flask API using Axios, then using the useState hook to use that data to build a table. The error I'm encountering is that after I use the setValue function to update the state, an extra "tableData:{id:0}" object is added to the JSON object the API returning, and it seems to happen after the setValue function is called.
I have already tried to use delete values.key.tableData when I was only dealing with single objects, but now that I have an array of objects it doesn't work anymore, and I don't understand why this key is even added in the first place.
I get an "undefined" error when I don't use the initial state like I did below.
The console shows the original string (not parsed yet) when i log the API's original response, but shows this when I log the state:
​
0: Object { Email: "", tableData: {…} }
​
length: 1
​
<prototype>: Array []
​
How do I keep the "tableData" key from being added to the response? Is there an issue with the way I'm using the hook?
The relevant parts of the code are down here:
React.useEffect(() => {
setLabelWidth(inputLabel.current.offsetWidth);
axios.get("http://127.0.0.1:5000/api",{params: { start: selectedstartDate, end: selectedendDate}}).then((response)=>{
console.log(response.data.details);
setValues(response.data.details);
console.log(values);
}, []);
After looking at the way MaterialTable behaves, the issue was created by it mutating whatever is passed as data, so in this case my state variable, and adding that extra key to render the table. To solve this, I added a second state variable which is a copy of the first one, that I only use for the table data prop, and kept using the original values for everything else.

Is using Immutable.set() with "deep" parameters in ImmutableJS safe?

Trying out immutable-js, I found something that I don't fully understand and want to make sure I'm using set correctly.
Basically, if you use "set()" to set a value of a property with an object or array, it stores a raw object/array, whereas if you use merge, it does what I'd expect, converting the raw array to an immutable list.
So my question is: Should you avoid using set() with objects/arrays for parameter 2?
Jasmine Test example here:
it("can do a thing you shouldn't do(?) - inject a normal object into a Map via set", function(){
const expected = fromJS({ a: [ 3, 4, 5 ] });
const set_value = expected.set("a", [3, 4,5]);
const merge_value = expected.merge({"a": [3, 4,5]});
expect(expected.get("a")).toEqualImmutable(set_value.get("a")); // Fails
expect(expected.get("a")).toEqualImmutable(merge_value.get("a")); // Passes
})
Output is:
Message:
Expected
List [ 3, 4, 5 ]
to equal
3,4,5
You should use set() with objects/arrays only when you want your Immutable objects to contain objects/arrays.
That is to say, this is a style choice without a definitive right answer.
A good rule of thumb is to not put mutable object into your immutable collections unless you know what you're doing and you have a really good reason to.
Is it safe?
That depends on what you mean by safe.
As of 4.0.0-rc8
getIn, setIn, updateIn, merge, toJS, etc. should all work with Immutable collections containing mutable data, so you're safe in that regard.
On the other hand, if you start putting mutable values in your immutable containers, you lose a lot of the benefits of having an immutable collection in the first place. You no longer have the guarantee that a reference to the same object will always contain the same data.

SearchBar deep search into JSON

I'm implementing a searchbar in IONIC 2 that search a JSON in one view so it can send its details to another view.
I have this JSON:
{
"Alphaville I": { //FIRST KEY
"ida": [{ //SECOND KEYS
"hora": "05:40",
"local": "AV. FERNÃO DIAS PAES LEME (Pref. Várzea Paulista)"
},... ],
"volta": [{ //SECOND KEYS
"hora": "05:40",
"local": "AV. FERNÃO DIAS PAES LEME (Pref. Várzea Paulista)"
},... ]
}, ... //MULTIPLE ITENS
}
So, in one view i create a list with the first keys (like Alphaville I), but i need to search the local inside of it.
But the Angular 2 *ngFor requires an array, so i iterate through my object and push it to an array, doing this it excludes my first key, so what i'm doing now (without searching, of course) is saving the keys in one array, geting the index and passing the jsonResultExample[index] to another page.
i'm using the basic searchbar example like the one in Seachbar Component Docs.
So what i need is: Search by the local key and return the first key (Alphaville I) of the nodes the contain the input text, the same local can appear in other first keys.
How can i do this? I can't post a better code because i haven't tried anything.
Is there a better way to structure my JSON for this? (i'm using firebase btw);
Any help or ideas is welcome, thanks.
EDIT
So i saved the first key value along with ida and volta so i can simply iterate through it, get the key value and everything without many problems, but since i need to filter by local it appears inside idaand volta as another array (cause i have many of these values), so it's looking like this now:
So now how can i access the local? Is it better to create another object only with all local and a key for every linha so i can return the values?
Remembering this is the searchbar code for Ionic 2 and my JSON has over 4k lines:
getItems(ev: any) {
// Reset items back to all of the items
this.initializeItems();
// set val to the value of the searchbar
let val = ev.target.value;
// if the value is an empty string don't filter the items
if (val && val.trim() != '') {
this.items = this.items.filter((item) => {
return (item.toLowerCase().indexOf(val.toLowerCase()) > -1);
})
}
}
Thanks in advance :)
It comes to personal experience on how to design data structure. Therefore I can't say the follow method is the best way.
First, in the case that we have complicated data structure, I don't prefer using map (a.k.a. object as data structure) in javascript. The main reason is pretty related to what you are facing, object by design cannot be iterated. Yes you can use Object.keys() or Object.values() but they are so ugly and hard to fit on every cases.
It is a nice move to put your first key as a property. That comes to the second problem. There seems to be an assumption in your structure that, one linha is mapped only to one local or one local is only related to one linha. If so, I suggest building another separated map only for the linha and local relationship.
Another approach is to normalize your data structure in to multiple separated javascript objects like what you do on database. By doing so, you can maximize the data flexibility that you can query whatever you want by Array.prototype.filter(), Array.prototype.map() or even directly access by its index. However, this approach may increase the lines of code as you need to manage multiple maps.

AS3/Flex apply sort to ArrayCollection only once

I've got an ArrayCollection that serves as a dataProvider for a list.
The collection stores objects of type MyObject:
public class MyObject {
public var myMap:Dictionary;
}
myMapstores key-value pairs, the key being an integer, the values are Strings.
So far for the constraints. What I want to do now is to sort the collection based on fields of the map.
Using a the ArrayCollection's sort function with my own compareFunction does work. This is how I've implemented it:
var key:int = 15;
var sort:Sort = new Sort();
sort.compareFunction = fidSort;
myCollection.sort = sort;
myCollection.refresh();
private function fidSort(a:Object, b:Object, fields:Array = null):int {
if(a.myMap[key].fieldValue == b.myMap[key].fieldValue) {
return 0;
} else if(a.myMap[key].fieldValue > b.myMap[key].fieldValue) {
return 1;
} else{
return -1;
}
}
As I said, that does work for the sake of sorting. However, naturally the sort (being a property of the collection) remains on the collection unless specifically removed from it, which means that every time a value in the map of MyObject changes, it will get sorted according the comparefunction.
What I need is to apply the sort exactly once, what happens afterwards with the map values shouldn't change the collections sorting.
I've tried things like disabling autoupdate on the colleciton (naturally that won't work as the collection doesn't get any updates any more (well it does, but they are cached only)).
After that I've read this post about sorting the underlying array.
However, that doesn't seem to work with the map, as I do get a compile error saying that the myMap[key].fieldValue couldn't be found on MyObject.
So yes, I'm kinda lost in space here. If someone has a clue how to achieve this, very basic task really, please let me know.
Cheers!
Got it, and for the sakes of completeness, I'd like to answer this question myself.
As said before, using myCollection.toArray().sort(fidSort) didn't work completely. The array made in this step has indeed been sorted, the collection, however, didn't get the sort, even though refresh() has been called.
To fix this, instead of creating a new array from the collection, we need to directly use the collection's source (which is an array of course) and sort that array;
collection.source.sort(fidSort);
collection.refresh();
Since we are still only sorting the array and not applying the Sort to the collection itself, the collection is sorted only once, regardless of the updates to it's data.
Edit: Just for kicks, restoring the original item positions isn't possible out of the box when sorting the collection's underlying array like it can be done when applying a sort on an ArrayCollection directly and setting it to null to restore the positions.
Simple solution is to cache the array item indices beforehand.