Related
I'm stuck with something I thought would be easy. Let's say I have an object like this. I'm trying to insert in the div each name of the animal tagged and the number of times that tag is in types (for example, cat = 3, etc...)
var animals = '';
animals = {
"types": [
{
"id": "1",
"tags": ["cat"]
},
{
"id": "2",
"tags": ["dog"]
},
{
"id": "3",
"tags": ["cat", "bird", "dog"]
},
{
"id": "4",
"tags": []
},
{
"id": "5",
"tags": ["cat", "bird"]
}
]
}
for (var i = 0; i < animals.length; i++) {
var tags = animals[i].tags;
}
<div class="types">Number of animals:</div>
I'm a beginner with complex JSON objects, any help would be appreciated. It can be vanilla JS or Jquery.
Thanks!
Check out the snippet below, first loop iterates and counts each animal.
Second populates your div
var animals = '';
animals = {
"types": [{
"id": "1",
"tags": ["cat"]
},
{
"id": "2",
"tags": ["dog"]
},
{
"id": "3",
"tags": ["cat", "bird", "dog"]
},
{
"id": "4",
"tags": []
},
{
"id": "5",
"tags": ["cat", "bird"]
}
]
}
var tags = {};
// Iterate over all your items
animals.types.forEach(function(type) {
// Iterate over all the animals in the array
type.tags.forEach(function(tag) {
if (tag in tags) {
// If animal is present, increment the count
tags[tag] = tags[tag] + 1;
} else {
// If animal is not present, add the entry
tags[tag] = 1;
}
})
})
// Iterate over all the animals and add it to the div
for (var animal in tags) {
if (tags.hasOwnProperty(animal)) {
document.getElementsByClassName('types')[0].innerHTML += ' ' + animal + ' ' + tags[animal];
}
}
<div class="types">Number of animals:</div>
You can do like this by using map() method :
var animals = {
"types": [{
"id": "1",
"tags": ["cat"]
},
{
"id": "2",
"tags": ["dog"]
},
{
"id": "3",
"tags": ["cat", "bird", "dog"]
},
{
"id": "4",
"tags": []
},
{
"id": "5",
"tags": ["cat", "bird"]
}
]
};
var count = {};
animals.types.map(function (arr, i) {
arr.tags.map(function (tag, k) {
count[tag] = (count[tag] || 0) + 1;
});
});
console.log(count);
If you use reduce & destrucuring it becomes one liner:
var animals = {
"types": [{
"id": "1",
"tags": ["cat"]
},
{
"id": "2",
"tags": ["dog"]
},
{
"id": "3",
"tags": ["cat", "bird", "dog"]
},
{
"id": "4",
"tags": []
},
{
"id": "5",
"tags": ["cat", "bird"]
}
]
}
console.log(
animals.types.reduce((r,{tags}) => tags.map(tag => r[tag] = (r[tag] || 0) + 1) && r, {})
)
Try this simple way:
var animals = { "types": [ { "id": "1", "tags": ["cat"] }, { "id": "2", "tags": ["dog"] }, { "id": "3", "tags": ["cat", "bird", "dog"] }, { "id": "4", "tags": [] }, { "id": "5", "tags": ["cat", "bird"] } ] }
var finalRes={};
animals.types.map(function(o, i){
o.tags.map(function(p, j){
finalRes[p]=(finalRes[p]||0)+1;
});
});
console.log(finalRes);
Result:
{ cat: 3, dog: 2, bird: 2 }
Sorry, I am typing with mobile phone, slow but correct!
Flatten all tags into single array
Count each tag
Handle tag count as you need
const animals = {
"types": [
{
"id": "1",
"tags": ["cat"]
},
{
"id": "2",
"tags": ["dog"]
},
{
"id": "3",
"tags": ["cat", "bird", "dog"]
},
{
"id": "4",
"tags": []
},
{
"id": "5",
"tags": ["cat", "bird"]
},
],
};
// Flatten all tags into single array
var allTags = [].concat(
...animals.types.map(
(type) => type.tags
)
);
// Count each tag
const tagsCount = {};
allTags.forEach(
(tag) => tagsCount[tag] = tagsCount[tag] ? tagsCount[tag] + 1 : 1
)
// Handle tag count as you need
const app = document.querySelector('#app');
app.innerHTML = Object.keys(tagsCount).map((key) => {
return `<p>${key}: ${tagsCount[key]}</p>`
}).join('');
<h1>Number of Animal Types</h1>
<div id="app"></div>
Basic javascript usage.
// var animals = ''; // not needed
var animals = {
"types": [{
"id": "1",
"tags": ["cat"]
},
{
"id": "2",
"tags": ["dog"]
},
{
"id": "3",
"tags": ["cat", "bird", "dog"]
},
{
"id": "4",
"tags": []
},
{
"id": "5",
"tags": ["cat", "bird"]
}
]
};
var counts = {};
for (var i = 0; i < animals.types.length; i++) { // types is a key in animals object, animals is not an array
var tags = animals.types[i].tags;
if (tags.length > 0) {
for (var j = 0; j < tags.length; j++) {
var tag = tags[j];
if (typeof counts[tag] === 'undefined') {
counts[tag] = 0;
}
counts[tag]++;
}
}
}
console.log(counts);
You could create a hash map for all the tags and increment the count whenever you encounter that tag in that types.tags array
Then loop through the object and append that into your HTML Element
var animals = '';
animals = {
"types": [
{
"id": "1",
"tags": ["cat"]
},
{
"id": "2",
"tags": ["dog"]
},
{
"id": "3",
"tags": ["cat", "bird", "dog"]
},
{
"id": "4",
"tags": []
},
{
"id": "5",
"tags": ["cat", "bird"]
}
]
}
let types = animals.types;
var counts = {};
for (var i = 0; i < types.length; i++) {
types[i].tags.forEach((x) => {
counts[x] = (counts[x] || 0)+1;
});
}
console.log(counts);
<div class="types">Number of animals:</div>
How Can I update an Array based on another Array?
This is how I tried to do:
var ref = [
{
"name": "Jack",
"title": "Manager",
"description": "",
},
{
"name": "Steve",
"title": "CEO",
"description": "A test description",
}
];
var elem = [
{
"name": "Jack",
"title": "Manager",
"description": "",
},
{
"name": "Steve",
"title": "CEO",
"description": "A test description",
}
];
for (var i = 0; i < ref.length; i++) {
ref.indexOf(elem[i]) === -1 ? ref.push(elem[i]) : console.log("This item already exists");
}
console.log(ref);
console.log(elem);
This Loop caused an error because the length is changing after each iterate.
What I want is, to add each element from elem object if it doesn't exist in arr object and of course stays in JOSN format. In my example, it doesn't have to change.
It would be better to run the loop over elem. Also you can't compare objects like that because references will differ, so you will have to do deep check, something like this should work:
var ref = [
{
"name": "Jack",
"title": "Manager",
"description": "",
},
{
"name": "Steve",
"title": "CEO",
"description": "A test description",
}
];
var elem = [
{
"name": "Jack",
"title": "Manager",
"description": "",
},
{
"name": "Steve",
"title": "CEO",
"description": "A test description",
}
];
for (var i = 0; i < elem.length; i++) {
var found = false;
for (var j=0; j<ref.length; j++){
var count = Object.keys(elem[i]).length;
for(var key in elem[i]){
if(ref[j][key] === elem[i][key])
count--;
}
if (count === 0){
found= true;
break;
}
}
if (!found)
ref.push(elem[i]);
}
console.log(ref);
console.log(elem);
To avoid nested loops, you could build a Set with each of the keys that you have in ref. Let's say you consider the name to be the identifying key, then it would look like this:
ref = ref.concat(elem.filter(function (o) {
return !this.has(o.name)
}, new Set(ref.map(o => o.name))));
So, this builds the Set by taking all the names in ref, passes that as this to a filter callback, which checks for each elem entry whether it has a name that is in the set. If so, it is excluded by the filter. This filtered result is concatenated to ref.
If your identifying key is something else, like the combination name and title, then adjust the function that is used in the last map(.......), and apply the same logic in has().
Here is a fiddle (with slightly different sample data) that uses the combination of name and title as identifying key:
var ref = [{
"name": "Jack",
"title": "Manager",
"description": "",
}, {
"name": "Steve",
"title": "CEO",
"description": "A test description",
}];
var elem = [{
"name": "Jack",
"title": "Manager",
"description": "",
}, {
"name": "Steve",
"title": "Programmer",
"description": "Java addict",
}];
ref = ref.concat(elem.filter(function (o) {
return !this.has(JSON.stringify([o.name, o.title]))
}, new Set(ref.map(o => JSON.stringify([o.name, o.title])))));
console.log(ref);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I have an object like this:
var Object = {
"id": "Siplus",
"name":"Siplus",
"icon":"forum"
},
{
"id": "Recent",
"name":"Recent Activities",
"icon": "restore"
},
{
"id": "jobList",
"name":"Job List",
"icon": "briefcase"
},
{
"id": "Favourites",
"name":"Favourites",
"icon": "star"
},
{
"id": "searchQuote",
"name":"Search Quotes",
"icon": "binoculars"
},
{
"id": "orderStatus",
"name":"Order Status",
"icon": "clock"
};
I have another array Like this
var array = [1,2,3];
I adding array values to object using this code:
for (var i = 0; i < object.length; i++) {
object[i].number = array[i];
}
I am getting result like this:
var Object = {
"id": "Siplus",
"name":"Siplus",
"icon":"forum",
"number":1
},
{
"id": "Recent",
"name":"Recent Activities",
"icon": "restore",
"number":2
},
{
"id": "jobList",
"name":"Job List",
"icon": "briefcase",
"number":3
},
{
"id": "Favourites",
"name":"Favourites",
"icon": "star",
"number":undefined
},
{
"id": "searchQuote",
"name":"Search Quotes",
"icon": "binoculars",
"number":undefined
},
{
"id": "orderStatus",
"name":"Order Status",
"icon": "clock",
"number":undefined
};
I wanted like this :
var Object = {
"id": "Siplus",
"name":"Siplus",
"icon":"forum",
"number":1
},
{
"id": "Recent",
"name":"Recent Activities",
"icon": "restore",
"number":2
},
{
"id": "jobList",
"name":"Job List",
"icon": "briefcase",
"number":3
},
{
"id": "Favourites",
"name":"Favourites",
"icon": "star",
"number":1
},
{
"id": "searchQuote",
"name":"Search Quotes",
"icon": "binoculars",
"number":2
},
{
"id": "orderStatus",
"name":"Order Status",
"icon": "clock",
"number":3
};
Is their any way to get repeat the number instead of getting "undefined"
Please help me for this
You could map your input objects by adding the right value from array thanks to the modulo calculus
var data = [{
"id": "Siplus",
"name":"Siplus",
"icon":"forum"
},
{
"id": "Recent",
"name":"Recent Activities",
"icon": "restore"
},
{
"id": "jobList",
"name":"Job List",
"icon": "briefcase"
},
{
"id": "Favourites",
"name":"Favourites",
"icon": "star"
},
{
"id": "searchQuote",
"name":"Search Quotes",
"icon": "binoculars"
},
{
"id": "orderStatus",
"name":"Order Status",
"icon": "clock"
}];
var array = [1,2,3];
res = data.map((x,i) => {
x.number = array[i % array.length]
return x;
})
console.log(res);
The size of array is 3 while that of object is more - a solution would be to use:
object[i].number = array[i % array.length];
See demo below:
var object=[{"id":"Siplus","name":"Siplus","icon":"forum"},{"id":"Recent","name":"Recent Activities","icon":"restore"},{"id":"jobList","name":"Job List","icon":"briefcase"},{"id":"Favourites","name":"Favourites","icon":"star"},{"id":"searchQuote","name":"Search Quotes","icon":"binoculars"},{"id":"orderStatus","name":"Order Status","icon":"clock"}]
var array = [1, 2, 3];
for (var i = 0; i < object.length; i++) {
object[i].number = array[i % array.length];
}
console.log(object);
.as-console-wrapper{top:0;max-height:100%!important;}
you could use an additional var.
for (var i = 0, j = 0; i < object.length; i++) {
j++
if(j > array.length){j=0}
object[i].number = array[j];
}
var arrLength = array.length;
for (var i = 0, j = 0; i < object.length; i++, j++) {
if (i >= arrLength ) {
j = 0;
}
object[i].number = array[j];
}
You could use a temporary value to point to the numbers array like this:
var temp = 0;
for (var i = 0; i < object.length; i++) {
object[i].number = array[temp];
if(temp == array.length)
temp = 0;
else
temp++;
}
I have the data as shown in step#1 and I would like to rearrange it in the form of parent with corresponding children as in step#2 based on the "id":
step#1:
[
{"id": "card:1.usa", "name": "usa"}, {"id": "card:2", "name": "card2"}, {"id": "card:1", "name": "card1"}, {"id": "card:2.washington", "name": "washington"},
{"id": "card:1.usa.illinios", "name": "illinios"}, {"id": "card:1.usa.illinios.city1", "name": "chicago"}
]
step#2 :
[
{"id": "card:1", "name": "card1", "children": [ {"id": "card:1.usa", "name": "usa", "children":[ {"id": "card:1.usa.illinios", "name": "illinios", "children":[ {"id": "card:1.usa.illinios.city1", "name": "chicago"}] }] } },
{"id": "card:2", "name": "card2", "children": [ {"id": "card:2.washington", "name": "washington" }] }
]
I tried to do the following from my side, but this gets only first level children:
var cardData = [
{"id": "card:1.usa", "name": "usa"}, {"id": "card:2", "name": "card2"}, {"id": "card:1", "name": "card1"}, {"id": "card:2.washington", "name": "washington"},
{"id": "card:1.usa.illinios", "name": "illinios"}, {"id": "card:1.usa.illinios.city1", "name": "chicago"}
]
var subCardList = [];
$scope.parentCard = [];
for(var i=0; i<cardData.length; i++){
if( cardData[i].id.indexOf('.') > -1){
subCardList.push( cardData[i] );
}
}
for(var i=0; i<cardData.length; i++){
for(var j=0; j<subCardList.length; j++){
var cardObj = {};
if( cardData[i].id == subCardList[j].id.substr(0, subCardList[j].id.indexOf('.')) ){ //found matching parent card
cardObj.id = cardData[i].id;
cardObj.children = subCardList[j];
$scope.parentCard.push( cardObj );
}
}
}
Please let me know how I can achieve this through javascript/jquery?
I have improved my solution to make it more comprehensive and also handle the situation where all cards in the hierarchy are not present and in different order.
A sorting is performed on the sub-card array based on id before processing. This ensures order of cards do not break this code.
All top level cards are collected first. Each sub-card is then placed into buckets(Children collection).
I have provided additional data set (cardData1) to highlight boundary conditions.
Please let me know if you need more explanation.
var cardData = [{
"id": "card:1.usa",
"name": "usa"
}, {
"id": "card:2",
"name": "card2"
}, {
"id": "card:1",
"name": "card1"
}, {
"id": "card:2.washington",
"name": "washington"
}, {
"id": "card:1.usa.illinios",
"name": "illinios"
}, {
"id": "card:1.usa.illinios.city1",
"name": "chicago"
}]
var cardData1 = [
{
"id": "card:1.usa.illinios.city1.municipality1",
"name": "DumDum"
},{
"id": "card:1.usa",
"name": "usa"
}, {
"id": "card:2",
"name": "card2"
}, {
"id": "card:1",
"name": "card1"
}, {
"id": "card:2.washington",
"name": "washington"
}, {
"id": "card:1.usa.illinios.city1",
"name": "chicago"
},
]
var subCardList = [];
var subCardMap = {};
var parentCardList = [];
var cardMap = {};
for (var i = 0; i < cardData.length; i++) {
if (cardData[i].id.indexOf('.') > -1) {
subCardList.push(cardData[i]);
subCardMap[cardData[i].id] = cardData[i];
} else {
//top level cards
var cardId = cardData[i].id;
var parentCard = {
id: cardId,
name: cardData[i].name,
children: []
};
cardMap[cardId] = parentCard;
parentCardList.push(parentCard);
}
}
//Sort the subcard list to ensure missing hierarchial cards do not break implementation
subCardList.sort(function (a, b) {
return a.id.toLowerCase().localeCompare(b.id.toLowerCase());
});
//Build buckets(children array) for child cards on the fly
for (var j = 0; j < subCardList.length; j++) {
var topCardId = subCardList[j].id.substr(0, subCardList[j].id.indexOf('.'));
placeInBucket(topCardId, subCardList[j]); //find matching parent card from map
}
function placeInBucket(topCardId, childCard) {
var topCard = cardMap[topCardId]; //get top card
var childIds = childCard.id.split(".");
childIds.splice(0, 1); //Remove the top card id
var childId = "";
var bucket = topCard.children; //Assign the initial bucket as the topcard children array
//loop through all the hierarchy and create complete hierarchy
for (var i = 0; i < childIds.length; i++) {
var key = topCardId + childId + "." + childIds[i];
if (!subCardMap[key]) {
childId += "." + childIds[i];
continue;
} //Do not build hierarchy for missing subcards in the id chain
var child = cardMap[key];
if (!child) {
bucket.push(childCard);
cardMap[key] = childCard; //Register new child to cardMap
break;
}
if (!child.children) child.children = []; //Add Children array to a leaf card if not existing
bucket = child.children;
childId += "." + childIds[i]; //Append the id to drill down the hierarchy
}
}
console.log(JSON.stringify(parentCardList));
The short answer: you need a recursive function, a function that calls itself to nest the children until you find an element that has no children.
So, first, you find all the parents in your list:
[
{"id": "card:2", "name": "card2"},
{"id": "card:1", "name": "card1"},
]
Then, for each of these objects, you run through the array again, finding descendants. For example, for card:1:
[
{"id": "card:1.usa", "name": "usa"},
{"id": "card:1.usa.illinios", "name": "illinios"},
{"id": "card:1.usa.illinios.city1", "name": "chicago"}
]
This is where we'll need recursivity: you need to repeat the same process again, taking this array as your input. So the "parents" you will find are:
[
{"id": "card:1.usa", "name": "usa"}
]
And the children:
[
{"id": "card:1.usa.illinios", "name": "illinios"},
{"id": "card:1.usa.illinios.city1", "name": "chicago"}
]
Since you still have children, you would repeat yet again, until you don't find any more children.
This example might not be as clean as it could be (I wasn't sure how to deal with the ids), but it works:
function nestChildren(list, startIndex){
var parents = [];
// Search list for "parents" --> objects with only one period-separated section
for(var i=0;i<list.length;i++){
var ids = list[i].id.substring(startIndex).split('.'); // Count only sections from parent id
if (ids.length == 1){
parents.push(list[i]);
}
}
for(var i=0;i<parents.length;i++){
var parent = parents[i];
var children = [];
// Search list for "children" --> objects with ids that start with the parent id
for(var j=0;j<list.length;j++){
if (list[j].id.indexOf(parent.id) == 0 && list[j].id != parent.id){
children.push(list[j]);
}
}
if (children.length){
// If there's any children, nest those children too
// Pass the found children as the "list" parameter
// and the parent's id length as the second (to count periods from that index)
parent.children = nestChildren(children, parent.id.length + 1);
}
}
return parents;
}
Working JSFiddle here.
I have an object type as below:
{
"1": {
"ref": "1",
"active": "1",
"showorder": "1",
"title": "Test 1"
},
"2": {
"ref": "2",
"active": "1",
"showorder": "2",
"title": "Test 2"
},
"3": {
"ref": "3",
"active": "1",
"showorder": "4",
"title": "Test 4"
},
"4": {
"ref": "4",
"active": "1",
"showorder": "9",
"title": "Test 9"
},
"5": {
"ref": "5",
"active": "1",
"showorder": "7",
"title": "Test 7"
}
}
On the basis of showorder property i need it to be arranged as follows:
{
"1": {
"ref": "1",
"active": "1",
"showorder": "1",
"title": "Test 1"
},
"2": {
"ref": "2",
"active": "1",
"showorder": "2",
"title": "Test 2"
},
"3": {
"ref": "3",
"active": "1",
"showorder": "3",
"title": "Test 3"
},
"4": {
"ref": "4",
"active": "1",
"showorder": "4",
"title": "Test 4"
},
"5": {
"ref": "5",
"active": "1",
"showorder": "5",
"title": "Test 5"
}
}
And here is my failed attempt thought i would pass the object to this function :
function reArrangeTilesObject(tilesObj){
var newObj = [];
for(var i in tilesObj){
var j = 1;
if(j == tilesObj[i].showorder){
newObj.push(tilesObj[i]);
j++;
}
}
return newObj;
}
Sorry if i am being unclear. The object is created in a random manner. So what i want is the object to be arranged as per the showorder property. so if showorder is 4 it should come in the key of 4 and not have the object of showorder 9. showorder 9 should come under the key of 9. Thanks
Steps:
Convert the object to an array (objects are unordered, arrays are ordered)
Sort the array
Code:
function reArrangeTilesObject(tilesObj) {
var tilesArray = [];
// Step 1 - convert to array
for (var i in tilesObj) {
tilesArray.push(tilesObj[i]);
}
// Step 2 - sort
tilesArray.sort(function(a, b) {
return a.showorder - b.showorder;
});
return tilesArray;
}
Result – an ordered array:
[
{"ref":"1","active":"1","showorder":"1","title":"Test 1"},
{"ref":"2","active":"1","showorder":"2","title":"Test 2"},
{"ref":"3","active":"1","showorder":"4","title":"Test 4"},
{"ref":"5","active":"1","showorder":"7","title":"Test 7"},
{"ref":"4","active":"1","showorder":"9","title":"Test 9"}
]
I may have misunderstood something but :
for (var i = 0; i < tilesObj; i++)
{
tilesObj[i].showOrder = i;
tilesObj[i].title = "Test "+i;
}
or
for (var i in tilesObj)
{
tilesObj[i].showOrder = tilesObj[i].ref;
tilesObj[i].title = "Test "+tilesObj[i].ref;
}
will do the job on the example I believe.
I can't quite understand if you're trying to rename the properties in the object array, or sort them into a new array.
If you're trying to sort:
function reArrangeTilesObject(tilesObj) {
var newObj = [];
for (var i = 1; i <= tilesObj.length; i++) {
for (var j in tilesObj) {
if (i == tilesObj[j].showorder) {
newObj.push(tilesObj[j]);
break;
}
}
}
return newObj;
}
Should add them to your newObj array based on their showorder property.
Try this one (demo):
function reArrangeTilesObject(tilesObj){
var result = {};
for(var key in tilesObj){
content = result[key] = tilesObj[key];
content.showorder = key;
content.title = "Test " + key;
}
return result;
}
It will return a new object in the original format (a Dictionary).
If you want to return an Array (and deal with possible empty indexes), just change var result = {}; into var result = [];
If you are just rewriting the showorder and title values this will work
function fix(o) {
for(var n in o) {
o[n].showorder = n;
o[n].title = "Test " + n;
}
}