SearchBar deep search into JSON - 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.

Related

In Foundry Slate, how do I dynamically index into an array or object using handlebars?

In Foundry's Slate application, I'd like to index into an array or other object based upon some seletion in another widget -- say, choosing a column from a dropdown.
How can I accomplish something like this?
You can use the lookup handlebars helper reference.
The lookup helper looks like the following {{lookup arrayName index}} and is equivalent to arrayName[index] in javascript.
This also works for objects as {{lookup objectName key}}.
And you can do it for nested things as well:
{{lookup a "b" "c"}}
where the context is { a: { b: { c: "test" } } }
Will return "test"
To simplify things, you can also write a small helper function that takes in the selection and the data, does any necessary work to produce the correct output and then returns that output to display elsewhere.

Immutable.js and shortcut for getting at a key entry

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.

Dynamically generating FormFlow from JSON file

I am try to utilize JSON data to dynamically generate a form flow. In the Improved Sandwich Bot, each field in the form flow is independent to each other. For example, no matter I choose what kind of sandwich, I can continue to choose any type of bread. The only way to add some customization is using the following code:
.Field(new FieldJson(schema, "Specials")
.SetType(null)
.SetActive((state) => (string)state["Length"] == "FootLong")
.SetDefine(async (state, field) =>
{
field
.AddDescription("cookie", "FreeCookie")
.AddTerms("cookie", "cookie", "FreeCookie")
.AddDescription("drink", "FreeDrink")
.AddTerms("drink", "drink", "FreeDrink");
return true;
}))
However, since different sandwich stores have different menus, the dependency between different fields varies a lot. For example,
Store A may say only Sandwich1 can have toppings1, 2, 3. And store B
may say only Bread1 can have cheese1, 2, 3.
So I don't want to use the code above to implement the logic. It is not scalable.
So is it possible to include those dependency relations in the JSON file? In that way, the form builder can directly build the form flow with certain dependency relation.
No, it's not possible at this point but it seems like a very good suggestion. You can give the feedback at https://feedback.botframework.com/.

SAPUI5 copy model and break binding?

On initialization I read an oData service to get a small list of values and I store the model for further use in the application.
sap.ui.getCore().setModel(oODataJSONModel, "xlist");
At multiple stages, I want to make a copy of the original model, make changes to the values list and use it in a Select drop down. I've tried multiple different things, but every time I update/delete the copied model values, it is instantly reflected in the original model. This seems like a simple ask, but is there a way to break the link between the original model and the copied model, ideally I want to keep the original list intact so that list can be re-used over and over, regardless of what changes are made to the copies?
var oModelCpy = new sap.ui.model.json.JSONModel();
var cpyModelArray = oOrigModel.getData();
cpyModelJsonData = { results : [ cpyModelArray ] };
oModelCpy.setData(cpyModelJsonData );
When I remove entries from the copy model, it also removes entries from the original model, which in this case is not what i want.
Any suggestions?
A better approach is to save your data in the success handler:
oODataJSONModel.read("/yourService",
null,
null,
false,
function(oData, oResponse){
var oODataJSONModel = new sap.ui.model.json.JSONModel();
oODataJSONModel.setData(oData);
this.getView().setModel(oODataJSONModel, "jsonModel");
}
);
EDIT
I just stumbled upon this question while I was browsing through the list of UI5 questions, and it dawned to me what is causing your underlying copy issue! :-)
If you copy an array of objects to a new array (which is also happens if you copy model data to another model), you won't get a new array with new objects
Instead, you actually will get a new array, but with references to the old objects. So any change you make to a value in an object inside an array in model 1, will end up having that same value in model 2
So, in effect, you need to create new objects based on the old ones. Luckily, you don't need costly for loops and hardcoded value-copying logic to achieve this; one single line should be ok.
Let's say your original data is referenced by an array aData.
You then copy this data (a true copy) to a new array using JSON:
var aDataCopy = JSON.parse(JSON.stringify(aData));
If you now set this aDataCopy as the data for your second model, it will not have any references to the old model anymore.
Hope this helps!
Try using jquery extend() method to make a copy of the data. I had similar troubles earlier.
var newObject = $.extend({},oldObject);
Try this for once. Find the reference at http://api.jquery.com/jquery.extend/

In CouchDB how do you take in parameters from REST call

Hi so I'm new to CouchDB looks great so far, but really struggling with what must be simple to do!
I have documents structured as:
{
"_id" : "245431e914ce42e6b2fc6e09cb00184d",
"_rev": "3-2a69f0325962b93c149204aa3b1fa683",
"type": "student",
"studentID": "12345678",
"Name": "Test",
"group: "A"
}
And would like to access them them with queries such as http://couchIP/student?group=A or something like that. Are Views what I need here? I don't understand how to take the parameter from the query in the Map functions in Views. example:
function(doc,req) {
if(req.group==='A'){
emit(doc.id, doc.name);
}
}
Is my understanding of how Couch is working wrong or what's my problem here? Thanks in advance, I'm sure this is Couch 101
Already read through http://guide.couchdb.org/ but it didn't really answer the question!
You need views to achieve the desired results.
Define the following map function inside a view of a design document. ( let's name the view "byGroup" and assume this lives in a design document named "_design/students" )
function(doc) {
if(doc.group){
emit(doc.group,null);
}
}
Results can be obtained from the following url
http://couchIP:5984/dbname/_design/students/_view/byGroup?startkey="A"&endkey="A"&include_docs=true
To have friendly url couchdb also provides url rewriting options.
You need to some further reading about views and the relevance that they return key/pair values.
It's not clear what you want to return from the view so I'll guess. If you want to return the whole document you'd create a view like:
function (doc) { emit(doc.group, doc) };
This will emit the group name as a key which you can lookup against, the whole doc will be returned as the value when you look it up.
If you want to just have access to the names of those users you want to do something like:
function (doc) { emit(doc.group, doc.name) };
Your question arises from a misconception about what a view does. Views use map/reduce to generate a representation of your data. You have no control of the output of your view in your query because the view is updated according to changes in your DB documents only.
Using a list is also not a good option. It may seem that you can use knowledge of your request in your list to generate a different output depending on the query parameters but this is wrong because couchdb uses ETags for caching and this means that most times you will get the same answer regardless of your list parameters since the underlying documents won't have changed. There is a trick though to fool couchdb in this case and this implies using two different alternating users but I wouldn't even try this way because surely there are easier ways to achieve your objectives and you can probably solve your problem using group as a key in your map function.