I need to access the second array from a JSON decoded string, but I am having no luck.
The entire JSON string is displayed in var RAW00, and then split into var RAW01 & var RAW02.
All 3 of these are for testing - RAW00 is identical to msg
When they are split - I can access either, depending on what variable I start of with, but when I use RAW00 I cannot access the tutor section.
I will provide more detail if required, but my question is:
How do I see and access the tutor array in the second $.each (nested) block??]
Thanks :-)
success: function(msg)
{
var test = "";
var raw00 = {
"allData": [
{
"class2": [
{
"tid": "1",
"name": "Monday 2"
},
{
"tid": "1",
"name": "Monday Test"
}
]
},
{
"tutor": [
{
"fname": "Jeffrey",
"lname": "Kranenburg"
},
{
"fname": "Jeffrey",
"lname": "Kranenburg"
}
]
}
]
};
var raw01 = {
"allData": [
{
"class2": [
{
"tid": "1",
"name": "Monday 2"
},
{
"tid": "1",
"name": "Monday Test"
}
]
}
]
};
var raw02 = {
"allData": [
{
"tutor": [
{
"fname": "Jeffrey",
"lname": "Kranenburg"
},
{
"fname": "Jeffrey",
"lname": "Kranenburg"
}
]
}
]
};
$.each(raw00.allData, function(index, entry)
{
$.each(entry.class2, function (index, data)
{
console.log(this.name);
test += '<tr><td>'+this.name+'</td>';
});
$.each(entry.tutor, function (index, data)
{
console.log(this.fname);
test += '<td>'+this.name+'</td></tr>';
});
$('#all-courses-table-content').html( test );
});
You need to check whether the current element of the array is an object with class2 property or tutor property.
$.each(raw00.allData, function(index, entry) {
if (entry.hasOwnProperty('class2')) {
$.each(entry.class2, function (index, data)
{
console.log(this.name);
test += '<tr><td>'+this.name+'</td>';
});
}
if (entry.hasOwnProperty('tutor')) {
$.each(entry.tutor, function (index, data)
{
console.log(this.fname);
test += '<td>'+this.fname+'</td></tr>';
});
}
$('#all-courses-table-content').html( test );
});
Things would probably be simpler if you redesigned the data structure. It generally doesn't make sense to have an array of objects when each object just has a single key and it's different for each. I suggest you replace the allData array with a single object, like this:
var raw00 = {
"allData": {
"class2": [
{
"tid": "1",
"name": "Monday 2"
},
{
"tid": "1",
"name": "Monday Test"
}
],
"tutor": [
{
"fname": "Jeffrey",
"lname": "Kranenburg"
},
{
"fname": "Jeffrey",
"lname": "Kranenburg"
}
]
}
};
Related
This question already has answers here:
Find all values by specific key in a deep nested object
(11 answers)
Closed 10 months ago.
I have this JSON array tree that can include any number of nested arrays:
const namesArrayTree = [
{
"name": "Peter"
},
{
"name": "folder1",
"isArray": true,
"namesArray": [
{
"name": "Paul"
},
{
"name": "folder2",
"isArray": true,
"namesArray": [
{
"name": "Mary"
},
{
"name": "John"
}
]
}
]
},
{
"name": "Mark"
}
]
I need to transform it to a flat array including only the names:
const namesArrayFlat = [ "Peter", "Paul", "Mary", "John", "Mark" ]
So I'm using this code to do the transformation:
const namesArrayTree = [
{
"name": "Peter"
},
{
"name": "folder1",
"isArray": true,
"namesArray": [
{
"name": "Paul"
},
{
"name": "folder2",
"isArray": true,
"namesArray": [
{
"name": "Mary"
},
{
"name": "John"
}
]
}
]
},
{
"name": "Mark"
}
] ;
function getNamesList(item) {
let name = item.name;
let isArray = item.isArray;
if (isArray) {
name = item.namesArray.map(getNamesList).join("\r\n");
}
return name;
}
const namesList = namesArrayTree.map(getNamesList).join("\r\n");
const namesArrayFlat = namesList.split("\r\n");
console.log(namesArrayFlat)
The code works well, but I would like to get rid of the extra steps to create a list with the names using join.("\r\n") and then convert to array using split("\r\n").
That is, I would like to reduce the code by removing the following:
function getNamesList(item) {
let name = item.name;
let isArray = item.isArray;
if (isArray) {
/* remove code to join by "\r\n" */
name = item.namesArray.map(getNamesList)
}
return name;
}
/* remove code to create "namesList" constant and remove code to join by "\r\n") */
const namesArrayFlat = namesArrayTree.map(getNamesList)
console.log(namesArrayFlat)
(The above code still returns a tree nested arrays structure)
Any ideas about how to get rid of the extra code? also any suggestions about how to improve the code would be great, thanks!
function getNamesList(item) {
return item.isArray ? item.namesArray.map(getNamesList) : item.name
}
const names = namesArrayTree.map(getNamesList).flat(Infinity)
console.log(names)
You can achieve this with an array reducer as follows:
const namesArray = [
{
"name": "Peter"
},
{
"name": "folder1",
"isArray": true,
"namesArray": [
{
"name": "Paul"
},
{
"name": "folder2",
"isArray": true,
"namesArray": [
{
"name": "Mary"
},
{
"name": "John"
}
]
}
]
},
{
"name": "Mark"
}
] ;
function reduceNamesList(list, item) {
if (item.isArray) {
return item.namesArray.reduce(reduceNamesList, list);
}
list.push(item.name)
return list
}
const namesList = namesArray.reduce(reduceNamesList, [])
console.log(namesList)
I have an array with objects, like the following.
b = {
"issues": [{
"fields": {
"status": {
"id": "200",
"name": "Backlog"
}
}
}, {
"fields": {
"status": {
"id": "202",
"name": "close"
}
}
}, {
"fields": {
"status": {
"id": "201",
"name": "close"
}
}
}]
};
I want to count how many issues have status close, and how many have backlog. I'd like to save the count in a new array as follows.
a = [
{Name: 'Backlog', count: 1},
{Name: 'close', count: 2}
];
I have tried the following.
b.issues.forEach(function(i) {
var statusName = i.fields.status.name;
if (statusName in a.Name) {
a.count = +1;
} else {
a.push({
Name: statusName,
count: 1
});
}
});
That however doesn't seem to be working. How should I implement this?
This is a perfect opportunity to use Array#reduce. That function will take a function that is applied to all elements of the array in order and can be used to accumulate a value. We can use it to accumulate an object with the various counts in it.
To make things easy, we track the counts in an object as simply {name: count, otherName: otherCount}. For every element, we check if we already have an entry for name. If not, create one with count 0. Otherwise, increment the count. After the reduce, we can map the array of keys, stored as keys of the object, to be in the format described in the question. See below.
var b = {
"issues": [{
"fields": {
"status": {
"id": "200",
"name": "Backlog"
}
}
}, {
"fields": {
"status": {
"id": "202",
"name": "close"
}
}
}, {
"fields": {
"status": {
"id": "201",
"name": "close"
}
}
}]
};
var counts = b.issues.reduce((p, c) => {
var name = c.fields.status.name;
if (!p.hasOwnProperty(name)) {
p[name] = 0;
}
p[name]++;
return p;
}, {});
console.log(counts);
var countsExtended = Object.keys(counts).map(k => {
return {name: k, count: counts[k]}; });
console.log(countsExtended);
.as-console-wrapper {
max-height: 100% !important;
}
Notes.
Array#reduce does not modify the original array.
You can easily modify the function passed to reduce to for example not distinguish between Backlog and backlog by changing
var name = c.fields.status.name;
into
var name = c.fields.status.name.toLowerCase();
for example. More advanced functionality can also easily be implemented.
Using ES6 Arrow functions you can do it with minimum syntax
var b = {
"issues": [{
"fields": {
"status": {
"id": "200",
"name": "Backlog"
}
}
}, {
"fields": {
"status": {
"id": "202",
"name": "close"
}
}
}, {
"fields": {
"status": {
"id": "201",
"name": "close"
}
}
}]
};
var countOfBackLog = b.issues.filter(x => {
return x.fields.status.name === "Backlog"
}).length
var countOfClose = b.issues.filter(x => {
return x.fields.status.name === "close"
}).length
a =[{Name: 'Backlog', count : countOfBackLog}, {Name: 'close', count : countOfClose}]
More about arrow functions here
You can write like this. It is dynamic.
var a = {};
for(var key in b["issues"]){
if(!a.hasOwnProperty(b["issues"][key].fields.status.name)){
a[b["issues"][key].fields.status.name] = 1;
}else{
a[b["issues"][key].fields.status.name] = a[b["issues"][key].fields.status.name]+1;
}
}
var c = [];
for(var key1 in a){
c.push({
name : key1,
count : a[key1]
});
}
Something like this should do the trick. Simply iterate over your data, keep 2 counters with the number of each type of issue, and create the data format you want in the end. Try it live on jsfiddle.
var b = {
"issues": [{
"fields": {
"status": {
"id": "200",
"name": "Backlog"
}
}
}, {
"fields": {
"status": {
"id": "202",
"name": "close"
}
}
}, {
"fields": {
"status": {
"id": "201",
"name": "close"
}
}
}]
};
var data = [];
for(var issue of b.issues){
var entryFound = false;
var tempObj = {
name: issue.fields.status.name,
count: 1
};
for(var item of data){
if(item.name === tempObj.name){
item.count++;
entryFound = true;
break;
}
}
if(!entryFound){
data.push(tempObj);
}
}
console.log(data);
I have this data from a csv file that i have to use in a dependant dropdown with jquery. I can't figure out if it is possible to nest the data i received for what i already have coded.
CSV file
Banco Tarjeta Cuotas Medio_Pago Coeficiente TEA CFT
Santander Visa 1 modulodepago2 1 0.00% 0.00%
Santander Visa 1 nps 1.0262 18.56% 22.84%
Frances Visa 1 modulodepago2 1 0.00% 0.00%
Frances Master 2 nps 1.0262 18.56% 22.84%
My json data comes like this
[{"banco":"Santander","tarjeta":"Visa","cuotas":"1","medio_pago":"modulodepago2",
"coeficiente":"1","tea":"0.00%","cft":"0.00%"},
{"banco":"Santander","tarjeta":"Visa","cuotas":"1","medio_pago":"nps",
"coeficiente":"1.0262","tea":"18.56%","cft":"22.84%"} ...
etc...
Is there a way i can nest this json data like this (+ adding unique names and id's)?
var myJson = {
"banco": [
{
"name": "Santander",
"id": "Santander",
"tarjeta": [
{
"name": "Visa",
"id": "SantanderVisa",
"cuotas": [
{
"name": "1",
"id": "SantanderVisa1",
"medio_pago": "modulodepago2"
"coeficiente": "1",
"tea": "0.00%",
"cft": "0.00%",
},
{
"name": "1",
"id": "SantanderVisa2",
"medio_pago": "nps"
"coeficiente": "1.0262",
"tea": "18.56%",
"cft": "22.84%",
}
]
}
]
},
{
"name": "Frances",
"id": "Frances",
"tarjeta": [
{
"name": "Visa",
"id": "FrancesVisa",
"cuotas": [
{
"name": "1",
"id": "FrancesVisa1",
"medio_pago": "modulodepago2"
"coeficiente": "1",
"tea": "0.00%",
"cft": "0.00%",
}
]
},
{
"name": "Master",
"id": "FrancesMaster",
"cuotas": [
{
"name": "2",
"id": "FrancesMaster2",
"medio_pago": "nps"
"coeficiente": "1.0262",
"tea": "18.56%",
"cft": "22.84%",
}
]
}
]
}
]
}
You will need to group by keys. An easy way to do this is to use Lodash or Underscore.js.
I used Papa Parse to convert the CSV data into JSON.
var csvData = $('#csv-data').text().trim();
var jsonData = Papa.parse(csvData, { delimiter:',', header:true }).data;
var transformedJson = {
banco : _.chain(jsonData)
.groupBy('Banco')
.toPairs()
.map(banco => {
return {
name : banco[0],
id: banco[0],
tarjeta : _.chain(banco[1])
.groupBy('Tarjeta')
.toPairs()
.map(tarjeta => {
return {
name: tarjeta[0],
id: banco[0] + tarjeta[0],
cuotas: _.map(tarjeta[1], cuota => {
return {
name: cuota['Cuotas'],
id: banco[0] + tarjeta[0] + cuota['Cuotas'],
medio_pago: cuota['Medio_Pago'],
coeficiente: cuota['Coeficiente'],
tea: cuota['TEA'],
cft: cuota['CFT']
}
})
};
})
}
}).value()
}
console.log(JSON.stringify(transformedJson, null, 2));
.as-console-wrapper { top: 0; max-height: 100% !important; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/PapaParse/4.1.4/papaparse.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
<textarea id="csv-data" style="display:none" rows="5" cols="72">
Banco,Tarjeta,Cuotas,Medio_Pago,Coeficiente,TEA,CFT
Santander,Visa,1,modulodepago2,1,0.00%,0.00%
Santander,Visa,1,nps,1.0262,18.56%,22.84%
Frances,Visa,1,modulodepago2,1,0.00%,0.00%
Frances,Master,2,nps,1.0262,18.56%,22.84%
</textarea>
try something like this
you get all medio_pago for the others objects you just use the object name.
I haven't tested it but I'm sure this will work for you.
var Json = ...
$.each(Json, function(i, item) {
alert(myJson[i].banco.tarjeta.cuotas.medio_pago);
});
I have got the following array of Usernames
Usernames = [
{
"id": 1,
"userName": "Jack",
"description": "jack is a nice guy",
"userRoleIds": [
1
]
},
{
"id": 2,
"userName": "Caroline",
"description": "Good girl",
"userRoleIds": [
2,3
]
},
{
"id": 3,
"userName": "Smith",
"description": "Smithyyyy",
"userRoleIds": [
1,2
]
}
]
And an array of userRoles.
userRoles = [
{
id: 1,
roleName: "Admin"
},
{
id: 2,
roleName: "Tester"
},
{
id: 3,
roleName: "Developer"
}
]
What i want to get done is first concat the arrays in in Usernames and userRoles to get the following result.
Usernames = [
{
"id": 1,
"userName": "Jack",
"description": "jack is a nice guy",
"userRoleIds": [
{
"id": 1,
"roleName" : "Admin"
}
]
},
{
"id": 2,
"userName": "Caroline",
"description": "Good girl",
"userRoleIds": [
{
"id": 2,
"roleName" : "Tester"
},
{
"id": 3,
"roleName" : "Developer"
}
]
},...
The second thing i want is to be able to filter for the roleName and userName seperated by pipe signs. As in type something in a text box that searches for userName and roleName for example.
if i type
Caroline, Tester
The result will be
result = [
{
"id": 2,
"userName": "Caroline",
"description": "Good girl",
"userRoleIds": [
2,3
]
},
{
"id": 3,
"userName": "Smith",
"description": "Smithyyyy",
"userRoleIds": [
1,2
]
}
]
What is the best practice for achieving this?
Thanks
Here is how I would do it. I prefer using services and take advantage of their functions to keep code clean.
app.service('UserService', function (PermisionsServices) {
var self = {
'list': [],
'load': function (Users) {//Pass your array of Users
angular.forEach(Users, function (user) {
angular.forEach(user.userRoleIds, function (role) {
self.user.userRolesIds.push(PermisionsServices.get(role));
});
self.list.push(user);
});
}, 'get': function (id) {
for (var i = 0; i < self.list.length; i++) {
var obj = self.list[i];
if (obj.id == id) {
return obj;
}
}
}
};
return self;
});
app.service('PermisionsServices', function () {
var self = {
'list': [],
'load': function (permisions) {//Pass your array of permisions
angular.forEach(permisions, function (permision) {
self.list.push(permision);
});
}, 'get': function (id) {
for (var i = 0; i < self.list.length; i++) {
var obj = self.list[i];
if (obj.id == id) {
return obj;
}
}
}
};
return self;
});
Afterwards, you can use it on your controller:
$scope.users=UserService;
And access each of the users as a separate object which can have multiple object permisions.
NOTE: Building the service (populating it) will of course depend on your app logic and controller, you could just easily remove the "load" function and just hardcode the list object by copy and pasting your arrays.
This is the approach I use to load data from API via resource.
Regards
Edit:
For use on the UI, you would just call:
<div ng-repeat='user in users.list'>
{{user.name}} has {{user.permissions}}
</div>
as the object information is already contained within it.
Edit 2:
If you want to search your data, then you can just add a filter like this:
<div ng-repeat='user in users.list | filter: filterList'>
{{user.name}} has {{user.permissions}}
</div>
And then on the controller:
$scope.filterList = function (user) {
if ($scope.filterTextBox) {
return user.name.indexOf($scope.filterTextBox) == 0;
}
return true;
}
Hope this works for you
I would do with pure JS like this. It won't take more than a single assignment line each.
var Usernames = [
{
"id": 1,
"userName": "Jack",
"description": "jack is a nice guy",
"userRoleIds": [
1
]
},
{
"id": 2,
"userName": "Caroline",
"description": "Good girl",
"userRoleIds": [
2,3
]
},
{
"id": 3,
"userName": "Smith",
"description": "Smithyyyy",
"userRoleIds": [
1,2
]
}
],
userRoles = [
{
id: 1,
roleName: "Admin"
},
{
id: 2,
roleName: "Tester"
},
{
id: 3,
roleName: "Developer"
}
],
modified = Usernames.reduce((p,c) => (c.userRoleIds = c.userRoleIds.map(e => e = userRoles.find(f => f.id == e)),p.concat(c)),[]),
query = ["Caroline","Tester"],
filtered = modified.filter(f => query.includes(f.userName) || f.userRoleIds.some(e => query.includes(e.roleName)));
console.log(JSON.stringify(modified,null,2));
console.log(JSON.stringify(filtered,null,2));
You can use lodash to achieve this.
var role = _.find(userRoles, function(role) {
return role.roleName == 'Tester';
});
_.find(Usernames, function(user) {
return user.userName == 'Caroline' || _.indexOf(user.userRoleIds, role.id)>=0;
});
I want to combine multiple json data into a single vm. I read that you can map from js into the model multiple times and it should be merging but on my case, it's not. It's replacing the data.
function Item(ID, Name, Description) {
this.ID = ko.observable(ID);
this.Name = ko.observable(Name);
this.Description = ko.observable(Description);
}
var MasterViewModel = {
model: ko.observableArray([])
};
$.getJSON(url, function (response) {
ko.mapping.fromJS(response["TM1.Cube"], Item, MasterViewModel.model);
ko.mapping.fromJS(response["TM1.Dimension"], Item, MasterViewModel.model);
})
ko.applyBindings(MasterViewModel);
And here is my json data
{
"LogicalName": "TM1.Model",
"ID": "12345",
"Name: "Sample",
"TM1.Cube": [
{
"LogicalName": "TM1.Cube",
"ID": "111111",
"Name": Assets"
},
{
"LogicalName": "TM1.Cube",
"ID": "111112",
"Name": Finance"
}
],
"TM1.Dimension": [
{
"LogicalName": "TM1.Dimension",
"ID": "222221",
"Name": Assets"
},
{
"LogicalName": "TM1.Dimension",
"ID": "222222",
"Name": Finance"
}
]
}
and the outcome I expected is like this
{
"LogicalName": "TM1.Cube",
"ID": "111111",
"Name": Assets"
},
{
"LogicalName": "TM1.Cube",
"ID": "111112",
"Name": Finance"
},
{
"LogicalName": "TM1.Dimension",
"ID": "222221",
"Name": KPI"
},
{
"LogicalName": "TM1.Dimension",
"ID": "222222",
"Name": Default"
}
I have added a jsFiddle http://jsfiddle.net/e1ppj3qc/1/
The mapping plugin can take an existing model but I don't believe it will merge data.
For example you could map twice like so:
{one: "yo"}
and
{two: "dawg"}
into the same model and you would now have two observables, one() and two()
But if you was to do this (which you are):
{one: ["yo"]}
and
{one: ["dawg"]}
it will always overwrite the matched properties.
You could instead do the mapping and then simply push into the array that you want to add to like so:
function pushCubs(dataToPush) {
ko.utils.arrayPushAll(MasterViewModel.modelcub, dataToPush());
}
pushCubs(ko.mapping.fromJS(data));
pushCubs(ko.mapping.fromJS(data2));
http://jsfiddle.net/e1ppj3qc/2/