I have 2 endpoints, and each of them returns a JSON (actually 3 but let's make it simple). I can't combine them on the server side because I don't have access to it.
I need to display the data in an Ext.grid.Panel which accepts only one Store object. I tried making a model for each JSON and then somehow combining them, but I failed. So I'm thinking of joining them with a where clause, and I need to match the id from one JSON to bar_id from another JSON. For example like this:
{ "success": true,
"total": 3,
"message": "Data loaded",
"data": [
{"id": "1", "object_name": "foo1", "bar_id": 1},
{"id": "2", "object_name": "foo2", "bar_id": 2},
{"id": "3", "object_name": "foo3", "bar_id": 3}
]
}
And the other one:
{ "success": true,
"total": 5,
"message": "Data loaded",
"data": [
{"id": "1", "bar_name": "bar1"},
{"id": "2", "bar_name": "bar2"},
{"id": "3", "bar_name": "bar3"}
]
}
And I want to combine them like this:
[
{"id": "1", "object_name": "foo1", "bar_id": 1, "bar_name": "bar1"},
{"id": "2", "object_name": "foo2", "bar_id": 2, "bar_name": "bar2"},
{"id": "3", "object_name": "foo3", "bar_id": 3, "bar_name": "bar3"}
]
So I need something like: where FirstModel.bar_id equals SecondModel.id. Then I need to make a Store from this JSON. As you can see, I'm just starting with Ext JS.
Cao Urose,
Use one model based on combining your two response data arrays. Combining your two arrays is more a js issue than it is extjs issue. When you join your two data arrays, it will be straightforward to create a model and load your store.
Related
I have an array of nested objects. I am trying to iterate though the array and get back a list of ids from the nested objects.
"group": [
{
"groupId": "1",
"subGroup": {
"id": "44",
"name": "Testing",
}
},
{
"groupId": "2",
"subGroup": {
"id": "45",
"name": "Testing",
}
},
{
"groupId": "3",
"subGroup": {
"id": "46",
"name": "Testing",
}
}
]
I am trying to return a list of ids like so => [44, 45, 46]
I tried const result = map(path("subGroup", "id"), group), but it did not produce the result I need.
The approach you've taken is fine, except that R.path expects an array of path indexes rather than multiple arguments.
map(path(["subGroup", "id"]), group)
Alternatively, you could also just use the map method of arrays to achieve the same result.
group.map(g => g.subGroup.id)
I'm using a Ramda to give structure to some data. However, I have been unable to access the data inside the compose.
It should map items with a level greater than 2, but this did not work
[keys, compose(map(map(find(propEq('level', > 2)))), values)]
I am trying to keep all items inside typeChild as unique.
Here is the ramda console to test it out (got to follow link through there, SO won't allow goo.gl links): http://dpaste.com/0SATTZK
const result = pipe(
pluck('type'),
groupBy(
pipe(
find(propEq('level', 1)),
propOr('NoLevel', 'name'),
)
),
converge(
zipWith(unapply(zipObj(['name', 'typeChild']))),
[keys, compose(map(map(find(propEq('level', 2)))), values)]
),
);
result(data)
Input data
[{
"title": "Apple",
"type": [{"name": "Food", "level": 1}, {"name": "Fruit", "level": 2}]
}, {
"title": "Tomato",
"type": [{"name": "Food", "level": 1}, {"name": "Fruit", "level": 2}]
}, {
"title": "Potato",
"type": [{"name": "Food", "level": 1}, {"name": "Vegetable", "level": 2}]
}, {
"title": "The Alchemist",
"type": [{"name": "Entertainment", "level": 1}, { "name": "Book", "level": 2}]
}, {
"title": "Superman",
"type": [{"name": "Entertainment", "level": 1}, {"name": "Movie", "level": 2}]
}, {
"title": "More facts",
"type": [{"name": "Foo", "level": 2}]
}, {
"title": "Superman",
"type": [{"name": "Bar", "level": 1}]
}
];
Desired output
[
{name: "Food", typechild: [{level: 2, name: "Fruit"}, {level: 2, name: "Vegetable"}]},
{name: "Entertainment", typechild: [{level: 2, name: "Book"}, {level: 2, name: "Movie"}]},
{name: "NoName", typechild: [{level: 2, name: "Foo"}]},
{name: "Bar", typechild: []}
]
Ok, I'm going to make a guess at what you're looking for.
Show me the data
First of all, you really need to demonstrate some part of your input data. I reduced it to this:
const data =[{
"title": "Apple",
"type": [{"name": "Food", "level": 1}, {"name": "Fruit", "level": 2}]
}, {
"title": "Tomato",
"type": [{"name": "Food", "level": 1}, {"name": "Fruit", "level": 2}]
}, {
"title": "Potato",
"type": [{"name": "Food", "level": 1}, {"name": "Vegetable", "level": 2}]
}, {
"title": "The Alchemist",
"type": [{"name": "Entertainment", "level": 1}, { "name": "Book", "level": 2}]
}, {
"title": "Superman",
"type": [{"name": "Entertainment", "level": 1}, {"name": "Movie", "level": 2}]
}, {
"title": "More facts",
"type": [{"name": "Foo", "level": 2}]
}, {
"title": "Superman",
"type": [{"name": "Bar", "level": 1}]
}
];
(Note that I removed the color properties from each type as they don't seem relevant to the discussion, but they wouldn't change anything.)
And I'm guessing from your attempt that output something like this would be desired:
[
{name: "Food", typechild: [{level: 2, name: "Fruit"}, {level: 2, name: "Vegetable"}]},
{name: "Entertainment", typechild: [{level: 2, name: "Book"}, {level: 2, name: "Movie"}]},
{name: "NoName", typechild: [{level: 2, name: "Foo"}]},
{name: "Bar", typechild: []}
]
Approaching this by composing functions
Here is one approach:
const levelEq = (n) => pipe(prop('level'), equals(n));
const topLevel = pipe(prop('type'), find(levelEq(1)));
const topLevelName = pipe(topLevel, propOr('NoName', 'name'));
const extract2ndLevel = pipe(pluck('type'), flatten, filter(levelEq(2)));
const convert = pipe(
groupBy(topLevelName),
map(extract2ndLevel),
map(uniq),
toPairs,
map(zipObj(['name', 'typechild']))
);
convert(data); //=> (the first output format above)
(Usually for those one-liners, instead of pipe, I would use compose, and reverse the order, but I also don't much like mixing compose and pipe in the same script. I definitely prefer pipe for the longer convert function. But switching any of these, or combining them would not change anything essential.)
The point is that this is built on function composition. Rather than trying to build it all at once, I write separate functions to do small jobs and combine them into more complex ones.
Note that this code will not gracefully handle bad data, and changing to do that might be a significant chore.
Note also that in the main function, I work one small step at a time. I can comment out subsequent steps to see the result of each individual step. I could also use R.tap if I liked.
Re-combining rarely a good idea
Each of those helper functions, except for the relatively simple levelEq is used only once. So they could be easily inlined. We could rewrite this code like this:
const convert = pipe(
groupBy(pipe(prop('type'), find(pipe(prop('level'), equals(1))), propOr('NoName', 'name'))),
map(pipe(pluck('type'), flatten, filter(pipe(prop('level'), gte(__, 2))), uniq)),
toPairs,
map(zipObj(['name', 'typechild']))
);
But to me that is an unreadable mess, and I wouldn't bother.
Better Documentation
If you are used to the Hindley-Milnar style type annotation, it might help to add type signatures to these functions, perhaps something like:
// Type :: {name: String, level: Int}
// :: Int -> (Type -> Bool)
const levelEq = (n) => pipe(prop('level'), equals(n));
// :: {type: [Type]} -> Type
const topLevel = pipe(prop('type'), find(levelEq(1)));
// :: {type: [Type]} -> String
const topLevelName = pipe(topLevel, propOr('NoName', 'name'));
// :: [{title: String, type: [Type}]}] -> [Type]
const extract2ndLevel = pipe(pluck('type'), flatten, filter(levelEq(2)));
// [{title: String, type: [Type]}] -> [{name: String, typechild: [Type]}]
const convert = pipe( /* ... */ )
(If these mean nothing to you, don't worry about it.)
Changing Output Format
But perhaps you really want something like this:
[
{"name": "Food", "typechild": ["Fruit", "Vegetable"]},
{"name": "Entertainment", "typechild": ["Book", "Movie"]},
{"name": "NoName", "typechild": ["Foo"]},
{"name": "Bar", "typechild": []}
]
This turns out to be a simple change:
const convert = pipe(
groupBy(topLevelName),
map(extract2ndLevel),
map(uniq),
map(pluck('name')), // <--- A single addition
toPairs,
map(zipObj(['name', 'typechild']))
);
Advantages of map
One thing we see in that last snippet is a sequence of consecutive map calls. Each of those is looping over the list separately. This makes for clean code, but if in your performance testing, you found that this additional looping was causing your problems, you could take advantage of the composition law associated with map, which, suitably translated, says that
pipe(map(f), map(g)) ≍ map(pipe(f, g))
So you could add this:
// :: [{title: String, type: [Type}]}] -> [String]
const foo = pipe(extract2ndLevel, uniq, pluck('name'));
And rewrite the main function like this:
// [{title: String, type: [Type]}] -> [{name: String, typechild: [Type]}]
const convert = pipe(
groupBy(topLevelName),
map(foo),
toPairs,
map(zipObj(['name', 'typechild']))
);
But the fact that I can't think of a good name for this new function makes me think that it's not a great abstraction; I would only choose to do this if actual performance testing demonstrated that the multiple iterations were a real-world problem.
Conclusion
Functional programming is about many things, but one of the key techniques is a relentless breaking down of everything into easily understood pieces. That's what I try to do with this solution. While we can break this to create single functions without dependencies ("Recombining..." above) that is rarely readable. On the other hand, this approach made it easy to alter our approach ("Change Output formats"), and, if necessary, to fix performance problems ("Advantages of map").
Wow, that should have been a blog post!
You can see much of this in action on the Ramda REPL.
I have external JSON file call datas. This is the body of that JSON file.
[
{"value": "1", "text": "aaa"},
{"value": "2", "text": "bbb"},
{"value": "3", "text": "ccc"},
{"value": "4", "text": "ddd"},
{"value": "5", "text": "eee"},
{"value": "6", "text": "fff"},
{"value": "7", "text": "ggg"},
{"value": "8", "text": "hhh"},
{"value": "9", "text": "iii"},
{"value": "10", "text": "jjj"}
]
I want to filter data from this JSON file according to following array "b" values.(b0, b1, b3 etc)
$scope.array = {"badge":"1,2,5,7","id":"0","b0":"1","b1":"2","b2":"5","b3":"7"}
Example:
This array have b0, b1, b2 and b3 those values are 1, 2, 5 and 7. Therefor I want to get only 1, 2, 5, 7 values arrays from datas JSON file and display text values of this array.
This array can be change with same format. Therefor I want to consider b+"number" parameters.
Example 1:
$scope.array = {"badge":"1,2,3,9","id":"0","b0":"1","b1":"2","b2":"3","b3":"9"}
Example 2:
$scope.array = {"badge":"1,2,7","id":"0","b0":"1","b1":"2","b2":"7"}
Example 3:
$scope.array = {"badge":"1,2,5,7,8,9","id":"0","b0":"1","b1":"2","b2":"5","b3":"7","b4":"8","b5":"9"}
I get that JSON external file using angularjs like this,
$http.get("/json/datas.json").success(function(data) {
$scope.datas= data;
});
Values are display using repeat.
<div ng-repeat="data in datas">
<span ng-bind-html="data.text"></span>
</div>
Display HTML body only
aaa
bbb
eee
ggg
One way to do it is to filter, map, and/or reduce the array that has the "bX" values to create a lookup table of IDs, then filter the main data array based on that lookup table. Except that that "array" isn't an array, it is a plain object, so you can't use array methods on it directly. So I'm calling Object.keys() to get its keys in an array, and then I've chosen to use .reduce() to create the lookup table based on the keys that have the right format:
var data = [ {"value": "1", "text": "aaa"}, {"value": "2", "text": "bbb"}, {"value": "3", "text": "ccc"}, {"value": "4", "text": "ddd"}, {"value": "5", "text": "eee"}, {"value": "6", "text": "fff"}, {"value": "7", "text": "ggg"}, {"value": "8", "text": "hhh"}, {"value": "9", "text": "iii"}, {"value": "10", "text": "jjj"} ]
var $scope = {} // demo $scope object
$scope.array = {"badge":"1,2,5,7","id":"0","b0":"1","b1":"2","b2":"5","b3":"7"}
var bVals = Object.keys($scope.array).reduce(function(a, c) {
if (/^b\d+$/.test(c))
a[$scope.array[c]] = true
return a
}, {})
console.log(bVals)
var filteredData = data.filter(function(v) { return bVals[v.value] })
console.log(filteredData)
You can use javascript prototype functions map and find to filter the data.
First get the batch properties to an array and map the array to find the relevant values
$scope.array = {"badge":"1,2,3,9","id":"0","b0":"1","b1":"2","b2":"3","b3":"9"}
var batchArr = $scope.array.badge.split(',');
$scope.result = batchArr.map(o=> $scope.datas.find(k=> k.value == o))
angular.module("app",[])
.controller("ctrl",function($scope,$sce){
$scope.datas = [
{"value": "1", "text": "aaa"},
{"value": "2", "text": "bbb"},
{"value": "3", "text": "ccc"},
{"value": "4", "text": "ddd"},
{"value": "5", "text": "eee"},
{"value": "6", "text": "fff"},
{"value": "7", "text": "ggg"},
{"value": "8", "text": "hhh"},
{"value": "9", "text": "iii"},
{"value": "10", "text": "jjj"}
]
$scope.array = {"badge":"1,2,3,9","id":"0","b0":"1","b1":"2","b2":"3","b3":"9"}
var batchArr = $scope.array.badge.split(',');
$scope.result = batchArr.map(o=> $scope.datas.find(k=> k.value == o))
console.log($scope.result)
$scope.trust = function(html){
return $sce.trustAsHtml(html);
}
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<div ng-repeat="data in result">
<span ng-bind-html="trust(data.text)"></span>
</div>
</div>
I would like to filter my JSON array according to JSON array. I have following JSON array.
$scope.datas = [
{"value": "1", "text": "aaa"},
{"value": "2", "text": "bbb"},
{"value": "3", "text": "ccc"},
{"value": "4", "text": "ddd"},
{"value": "5", "text": "eee"},
{"value": "6", "text": "fff"},
{"value": "7", "text": "ggg"},
{"value": "8", "text": "hhh"},
{"value": "9", "text": "iii"},
{"value": "10", "text": "jjj"}
];
I want to filter this data according to my next JSON array.
$scope.array = {"badge":"1,2,3,9"};
This array can be change with same format.
Example 1:
$scope.array = {"badge":"1,2,3,9"}
Example 2:
$scope.array = {"badge":"1,2,7"}
Example 3:
$scope.array = {"badge":"1,2,5,7,8,9"}
I want to filter datas JSON array according to array of badge.
Display HTML body only
aaa bbb eee ggg
My code is working but older IE are not working because of it has find function.
$http.get("datas.json").success(function(data) {
$scope.spitedata = response.data.badge.split(',');
$scope.badgers = $scope.spitedata.map(function(o){ return data.find(function(k){return k.value == o})});
});
I have an array of object like this
[
{"id": "1", "name": "test"},
{"id": "2", "name": "test2"},
{"id": "3", "name": "test3"}
]
I want to convert it to object list this
{
"1": {"name": "test"},
"2": {"name": "test2"},
"3": {"name": "test3"},
}
You may use reduce :
var obj = arr.reduce(function(m,o){ m[o.id]={name:o.name}; return m }, {});
Side note : please be sure to read and try to understand T.J.'s comment about JSON