Getting next and previous element of json array - javascript

I need help with my code. I want to have a previous and a next button, these will call a function viewBlogItem(direction,cat,blogid);
in that function i'll be reading out the json file, categorizing by the "category".
Every blogItem has a articleid and a category, if click the next button I want to have the next blogItem.articleid and have that one shown (I use append for that one). If the direction == "next", then it look if it has a next item in category, if not then hide $('.next'). Same goes for previous button $('.previous')
blogItems.json
{
"blogItem":[
{
"id": 1,
"title": "animals blog 1",
"category":"animals",
"text":"text",
"articleid":1
},
{
"id": 2,
"title": "lifestyle blog 1",
"category":"lifestyle",
"text":"text",
"articleid": 1
},
{
"id": 3,
"title": "animals blog 2",
"category":"animals",
"text":"text",
"articleid": 2
},
{
"id": 5,
"title": "animals blog 4",
"category":"dieren",
"text":"text",
"articleid":4
},
{
"id": 4,
"title": "animals blog 5",
"category":"animals",
"text":"text",
"articleid":3
}
]
}
jquery
function viewBlogItem(direction,cat,blogid) {
var id = "";
if(direction == "next"){
// code for showing next blogitem
//if no next then
$('').hide();
}
else{
// if no previous then hide
//code for showing previous blogitem
}
loadBlog(id);
}
function loadBlog(blogid){
$.getJSON('blogitems.json', function (data) {
$.each(data.blogItem, function (i, item) {
if (item.id == blogid) {
$('.view').append('all sorts of stuff');
return;
}
});
});
}
I would also like to have some suggestions for the structure my json.

How can I tell that there ain't another blog after or previous?
Look at the index of the current blog item and see if the next one is bigger than than the number of items in the array or if the previous one is less than 0.
var blogs = {
"blogItem": [{
"id": 1,
"title": "animals blog 1",
"category": "animals",
"text": "text",
"articleid": 1
}, {
"id": 2,
"title": "lifestyle blog 1",
"category": "lifestyle",
"text": "text",
"articleid": 1
}, {
"id": 3,
"title": "animals blog 2",
"category": "animals",
"text": "text",
"articleid": 2
}, {
"id": 5,
"title": "animals blog 4",
"category": "dieren",
"text": "text",
"articleid": 4
}, {
"id": 4,
"title": "animals blog 5",
"category": "animals",
"text": "text",
"articleid": 3
}]
};
var index = 0;
var item = blogs.blogItem[index];
var title = document.getElementById('title');
var text = document.getElementById('text');
var previous = document.getElementById('previous');
var next = document.getElementById('next');
displayItem(item);
previous.addEventListener('click', function() {
displayItem(blogs.blogItem[--index]);
});
next.addEventListener('click', function() {
displayItem(blogs.blogItem[++index]);
});
function displayItem(item) {
title.innerText = item.title;
text.innerText = item.text;
previous.disabled = index <= 0;
next.disabled = index >= blogs.blogItem.length -1;
}
[disabled] {
opacity: 0.5;
}
<link href="//cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.min.css" rel="stylesheet"/>
<div class="container">
<div>
<div id="title"></div>
<div id="text"></div>
</div>
<button id="previous">Previous</button>
<button id="next">Next</button>
</div>

Related

Remove item from nested array object in Javascript by key value

I have object like this:
{
"id": 1,
"name": "first",
"sections": [
{
"id": 1,
"title": "First section",
"contents": [
{
"id": "123",
"title": "Sample title 1",
"description": "<html>code</html>",
},
{
"id": "124",
"title": "Sample title 2",
"description": "<html>code</html>"
},
{
"id": "125",
"title": "Some other sample",
"description": "<html>code</html>"
}
]
},
{
"id": 2,
"title": "Second section",
"contents": [
{
"id": "126",
"title": "Sample title 126",
"description": "<html>code</html>"
},
{
"id": "127",
"title": "Sample title 127",
"description": "<html>code</html>"
}
]
}
]
}
I want to remove specific object from contents array by its id (all those ids are unique).
I can easily find element I want to remove, but I'm unable to find in which section this element is to splice it later.
obj.sections.forEach(function(section) {
section.contents.forEach((content) => {
if (content.id == 125) {
console.log(content)
console.log(section)
}
})
})
In above code console.log(sections) returns undefined. How can I get position in sections array which contains contents array that has specific id. For example, id: 125 would return sections position 0, so I can use splice to remove that element.
If my approach is completely wrong please point me in right direction, thanks :)
You could use .filter() instead of .splice(). .filter() will keep all items which you return true for and discard of those which you return false for. So, if the current section's content's object has an id equal to the one you want to remove you can return false to remove it, otherwise return true to keep that item. You can use this with .map() to map each section object to a new one with an updated contents array:
const obj = { "id": 1, "name": "first", "sections": [ { "id": 1, "title": "First section", "contents": [ { "id": "123", "title": "Sample title 1", "description": "<html>code</html>", }, { "id": "124", "title": "Sample title 2", "description": "<html>code</html>" }, { "id": "125", "title": "Some other sample", "description": "<html>code</html>" } ] }, { "id": 2, "title": "Second section", "contents": [ { "id": "126", "title": "Sample title 126", "description": "<html>code</html>" }, { "id": "127", "title": "Sample title 127", "description": "<html>code</html>" } ] } ] };
const idToRemove = 125;
obj.sections = obj.sections.map(
sec => ({...sec, contents: sec.contents.filter(({id}) => id != idToRemove)})
);
console.log(obj);
.as-console-wrapper { max-height: 100% !important; top: 0; } /* ignore */
Why not use a simple filter for accomplishing the task. Also, it will be much cleaner and immutable way of doing it.
let newArrWithoutContentWithGivenId = obj.sections.map(section =>
({...section, contents: section.contents.filter(content =>
content.id != 125)}));
Here, we are mapping each section whose content does not contain the ID 125. In short, section > content.id != 125 will be removed from the new array.
Hope it helps :)
Note: Code is not tested, it is just to help you find a way to do it cleanly.
You only need to use the second argument of forEach :)
obj.sections.forEach(function(section, sectionIndex) {
section.contents.forEach((content, contentIndex) => {
if (content.id == 125) {
// use the sectionIndex and contentIndex to remove
}
})
})

How to remove items from an array of multidimensional arrays

I need some help on how to remove items from a TreeView (it's a Vue.js project), the TreeView is build based on an element like that:
[
{
"id": 1,
"name": "COMERCIALIZAÇÃO",
"idp": "",
"children": [
{
"id": 5,
"name": "Pasta 1",
"idp": 1,
"children": [
{
"id": 6,
"name": "Pasta 1 2",
"idp": 5,
"children": [
{
"id": 7,
"name": "NO.FT.DRC.01.00.001.pdf",
"file": "pdf",
"idp": 6
},
{
"id": 8,
"name": "PR.FT.DRC.01.00.003.pdf",
"file": "pdf",
"idp": 6
}
]
},
{
"id": 9,
"name": "imprimir p luiza.pdf",
"file": "pdf",
"idp": 5
},
{
"id": 66,
"name": "Pasta 1 3",
"idp": 5,
"children": [
{
"id": 77,
"name": "NO.FT.DRC.01.00.001.pdf",
"file": "pdf",
"idp": 66
},
{
"id": 88,
"name": "PR.FT.DRC.01.00.003.pdf",
"file": "pdf",
"idp": 66
}
]
}
]
},
{
"id": 10,
"name": "Backend.docx",
"file": "pdf",
"idp": 1
},
{
"id": 0,
"name": "DT.DC.RPI.03.03.1235_V2.docx",
"file": "pdf",
"idp": 1
}
]
},
{
"id": 2,
"name": "DISTRIBUIÇÃO",
"idp": "",
"children": [
{
"id": 11,
"name": "Pasta 2",
"idp": 2,
"children": [
{
"id": 12,
"name": "pasta 2 1",
"idp": 11,
"children": [
{
"id": 13,
"name": "script.sql",
"file": "pdf",
"idp": 12
}
]
}
]
}
]
},
{
"id": 3,
"name": "GERAÇÃO",
"idp": "",
"children": [
{
"id": 14,
"name": "Pasta 3",
"idp": 3
}
]
},
{
"id": 4,
"name": "SERVIÇOS",
"idp": "",
"children": [
{
"id": 5,
"name": "teste",
"idp": 4
}
]
}
]
I'm not sure, but I think that the best way to describe that element is: array of mutidimensional arrays, right?
I've created a CodePen to show the closest I got when using recursivity, but surely mine isn't the best solution since it doesn't work on every delete. Take a look at my code: https://codepen.io/luizarusso/pen/zYxLOPb?editors=1010
for (let i = 0; i < items.length; i++) {
if (items[i].id == item.id) {
//se achou o cara que vai ser removido, chama a função de remover
return this.removeItem(i);
} else {
if (items[i].children) {
if (items[i].idp == "") {
this.caminho = [];
}
this.caminho.push(i);
this.delFile(item, items[i].children);
} else {
if (items.length == 1 + i) {
this.caminho.pop();
}
}
}
}
Any ideas? Feel free to optimize my code directly on CodePen if you prefer :)
EDIT: Just to clarify, my problem here is strictly on how to remove an element by the id. When the user clicks on the bin icon I know what element I need to remove, but I don't know how to take it off of the array. Map, Filter and other native JS functions cannot do that to an array of arrays/JSON, so I tought about using recursivity or something else to make it work.
You need to look at objects, not just arrays.
Let me recommend an example library. https://github.com/leezng/vue-json-pretty.
If your question about multidimensional array iteration and process i think you have to ask on javascript and/or algorithm tags.
I hope this answer will help you.
The problem was with where I placed the this.caminho.pop()
I should only do that in the "else" of the condition that compares the id of the current item with the id of the item I'm looking for.
delFile(item, items) {
for (let i = 0; i < items.length; i++) {
if (items[i].id == item.id) {
//if the current item has the same id as the item I'm looking for
//it means I found the guy and I call the function to remove it
return this.removeItem(i);
} else {
//otherwise, I keep on searching
if (items[i].children) {
//if the item on the actual index have children, I'll search among them
if (items[i].idp == "") {
//if the items doesn't have a parent, I clean the "caminho" (path) var. That var traces the route till the item I'm looking for
this.caminho = [];
}
//I push the index to the var that traces the route
this.caminho.push(i);
//I call the function back again, now with the child items
this.delFile(item, items[i].children);
}
if (items.length == 1 + i) {
//if the item's lenght has been completely coursed, I pop the index out of the var that holds the route, because at this point I know the item I'm looking for is not among them
this.caminho.pop()
}
}
}
},
Here is the solution: https://codepen.io/luizarusso/pen/zYxLOPb
Works with treeview with any deepness

Extracting array from nested object properties by nest level

I have the following JSON of structured, nested data.
[
{
"id": "2e270ad7-90aa-41da-bb57-a777448f5906",
"name": "First Level 1",
"childValues": [
{
"id": "4cecbd28-fd06-4c2a-9b57-33d4a298675c",
"name": "Second Level 1 "
},
{
"id": "09893799-e21c-498f-96b4-e63e366a3c18",
"name": "Second Level 2"
}
]
},
{
"id": "18889675-9d71-420e-84a6-3603af044b6c",
"name": "First Level 2",
"childValues": [
{
"id": "b7093ca1-5fed-4eb2-b934-637bfdc6c7da",
"name": "Second Level 3"
},
{
"id": "a3575212-1746-4dd3-ab52-4e37786c035c",
"name": "Second Level 4"
}
]
},
{
"id": "71113ffb-62f0-4d76-941f-974be3cd35cb",
"name": "First Level 3",
"childValues": [
{
"id": "160570a5-29aa-4fdb-bb16-d9d7637d0177",
"name": "Second Level 5",
"childValues": [
{
"id": "2df28cb9-9ac4-478c-a2a4-6dc5206c983b",
"name": "Third Level 1"
},
{
"id": "a974cfac-1e2c-461c-ab64-0f5dd9d1cf1e",
"name": "Third Level 2"
}
]
},
{
"id": "6e5947ea-2c47-4d2b-8ecd-6369c728e7db",
"name": "Second Level 6"
}
]
}
]
I am trying to extract an array of objects from this nested array structure based on a level of nesting. For instance, level 0 just gives me back all of the base object in the array, but if I ask for level 1, I am trying to get back an output of just the second level nested objects, under the childValues property, in a single array, like the following:
[
{
"id": "4cecbd28-fd06-4c2a-9b57-33d4a298675c",
"name": "Second Level 1 "
},
{
"id": "09893799-e21c-498f-96b4-e63e366a3c18",
"name": "Second Level 2"
},
{
"id": "b7093ca1-5fed-4eb2-b934-637bfdc6c7da",
"name": "Second Level 3"
},
{
"id": "a3575212-1746-4dd3-ab52-4e37786c035c",
"name": "Second Level 4"
},
{
"id": "160570a5-29aa-4fdb-bb16-d9d7637d0177",
"name": "Second Level 5",
"childValues": [
{
"id": "2df28cb9-9ac4-478c-a2a4-6dc5206c983b",
"name": "Third Level 1"
},
{
"id": "a974cfac-1e2c-461c-ab64-0f5dd9d1cf1e",
"name": "Third Level 2"
}
]
},
{
"id": "6e5947ea-2c47-4d2b-8ecd-6369c728e7db",
"name": "Second Level 6"
}
]
And if I ask for level 2, I should only get the third level objects:
[
{
"id": "2df28cb9-9ac4-478c-a2a4-6dc5206c983b",
"name": "Third Level 1"
},
{
"id": "a974cfac-1e2c-461c-ab64-0f5dd9d1cf1e",
"name": "Third Level 2"
}
]
The only thing I have figured out is how to completely flatten the structure recursively, but cant put my finger on how to extract a specific level.
private flat(array: any[]) {
let result: any[] = [];
array.forEach((a) => {
result.push(a);
if (Array.isArray(a.childValues)) {
result = result.concat(this.flat(a.childValues));
}
});
return result;
}
Here's a cleaner version of Shane Padgett's function:
const getArrayByNthLevel = (array, levelToGet, currentLevel=0) => array.reduce((retval, a) => {
levelToGet === currentLevel
? retval.push(a)
: Array.isArray(a.childValues)
? retval = retval.concat(getArrayByNthLevel(a.childValues, levelToGet, currentLevel + 1))
: false
return retval;
}, []);
I was able to achieve this with the following function.
function getArrayByNthLevelOfPropName(array, propName, levelToGet, currentLevel = 0) {
let result = [];
array.forEach((a) => {
if (levelToGet === currentLevel) {
result.push(a);
}
if (Array.isArray(a[propName]) && levelToGet !== currentLevel) {
result = result.concat(getArrayByNthLevelOfPropName(a[propName], propName, levelToGet, ++currentLevel));
currentLevel -= 1;
}
});
return result;
}
You can use flatMap like this:
const input=[{id:"2e270ad7-90aa-41da-bb57-a777448f5906",name:"First Level 1",childValues:[{id:"4cecbd28-fd06-4c2a-9b57-33d4a298675c",name:"Second Level 1 "},{id:"09893799-e21c-498f-96b4-e63e366a3c18",name:"Second Level 2"}]},{id:"18889675-9d71-420e-84a6-3603af044b6c",name:"First Level 2",childValues:[{id:"b7093ca1-5fed-4eb2-b934-637bfdc6c7da",name:"Second Level 3"},{id:"a3575212-1746-4dd3-ab52-4e37786c035c",name:"Second Level 4"}]},{id:"71113ffb-62f0-4d76-941f-974be3cd35cb",name:"First Level 3",childValues:[{id:"160570a5-29aa-4fdb-bb16-d9d7637d0177",name:"Second Level 5",childValues:[{id:"2df28cb9-9ac4-478c-a2a4-6dc5206c983b",name:"Third Level 1"},{id:"a974cfac-1e2c-461c-ab64-0f5dd9d1cf1e",name:"Third Level 2"}]},{id:"6e5947ea-2c47-4d2b-8ecd-6369c728e7db",name:"Second Level 6"}]}];
const getLevel = (arr = [], required, current = 0) =>
required === current
? arr
: arr.flatMap(a => getLevel(a.childValues, required, current + 1))
console.log("Second Level: \n ", getLevel(input, 1))
console.log("Third Level: \n ", getLevel(input, 2))
If flatMap is not supported, you can use
[].concat(...arr.map(a => getLevel(a.childValues, required, current + 1)))
Following should work:
var data = [
{
"id": "2e270ad7-90aa-41da-bb57-a777448f5906",
"name": "First Level 1",
"childValues": [
{
"id": "4cecbd28-fd06-4c2a-9b57-33d4a298675c",
"name": "Second Level 1"
},
{
"id": "09893799-e21c-498f-96b4-e63e366a3c18",
"name": "Second Level 2"
}
]
},
{
"id": "18889675-9d71-420e-84a6-3603af044b6c",
"name": "First Level 2",
"childValues": [
{
"id": "b7093ca1-5fed-4eb2-b934-637bfdc6c7da",
"name": "Second Level 3"
},
{
"id": "a3575212-1746-4dd3-ab52-4e37786c035c",
"name": "Second Level 4"
}
]
},
{
"id": "71113ffb-62f0-4d76-941f-974be3cd35cb",
"name": "First Level 3",
"childValues": [
{
"id": "160570a5-29aa-4fdb-bb16-d9d7637d0177",
"name": "Second Level 5",
"childValues": [
{
"id": "2df28cb9-9ac4-478c-a2a4-6dc5206c983b",
"name": "Third Level 1"
},
{
"id": "a974cfac-1e2c-461c-ab64-0f5dd9d1cf1e",
"name": "Third Level 2"
}
]
},
{
"id": "6e5947ea-2c47-4d2b-8ecd-6369c728e7db",
"name": "Second Level 6"
}
]
}
];
function getData(data, targetLevel, currentLevel = 0) {
// If this is the target level, then extract the
// data we need from each item, and return the array
if (currentLevel == targetLevel) {
return data;
}
// Otherwise, run a map over the items, and if they have
// 'childValues', then recurs, but increment the value of
// 'current level' it will be iterating on
// Because 'map' will return array of array, merge them
// to a single array
return [].concat(...data.map(item => {
if (item.childValues) {
return getData(item.childValues, targetLevel, currentLevel + 1);
};
return [];
}));
}
document.getElementById("dataLevel0").innerHTML = JSON.stringify(getData(data, 0), null, 4);
document.getElementById("dataLevel1").innerHTML = JSON.stringify(getData(data, 1), null, 4);
document.getElementById("dataLevel2").innerHTML = JSON.stringify(getData(data, 2), null, 4);
<div>
Level 1:
</div>
<pre id="dataLevel0">
</pre>
<div>
Level 2:
</div>
<pre id="dataLevel1">
</pre>
<div>
Level 3:
</div>
<pre id="dataLevel2">
</pre>

Javascript - grab value inside of an object nested within an object

I am trying to grab a value of a key inside of an object in an array which itself is an object in an array.
Here is what it looks like:
var books = [
{
"title": "title1",
"author": "author1",
"users": [
{
"id": 1,
"name": "Isidro"
},
{
"id": 4,
"name": "Jose Miguel"
},
{
"id": 3,
"name": "Trinidad"
}
]
},
{
"title": "title2",
"author": "author2",
"users": [
{
"id": 4,
"name": "Jose Miguel"
},
{
"id": 5,
"name": "Beatriz"
},
{
"id": 6,
"name": "Rosario"
}
]
},
What I am trying to do, 2 things:
First:
when I click on a user name in the HTML, I want to match the name clicked with the same user name in all the objects it is present in.
Second:
display the title of the books this user name is present in.
For example: when I click on Jose Miguel I want to see the 2 books he has read.
At the moment I have this:
var btnUser = document.querySelectorAll(".individualUsers");
for (var i = 0; i < btnUser.length; i++) {
btnUser[i].addEventListener("click", function() {
var clickedUser = this.innerText
var userBooks = books
.filter(x => x.users.name.indexOf(clickedUser) > -1)
.map(x => ` <li>${x.title}</li> <li>${x.author}</li>`);
console.log(clickedUser);
});
}
My problem is x.users.name.indexOf(clickedUser)is not accessing the user name.
You need to search inside the users array as well, one neat way is to do so with Array.some that return true if some of the conditional is true.
const books = [{
"title": "title1",
"author": "author1",
"users": [{
"id": 1,
"name": "Isidro"
},
{
"id": 4,
"name": "Jose Miguel"
},
{
"id": 3,
"name": "Trinidad"
}
]
},
{
"title": "title2",
"author": "author2",
"users": [{
"id": 4,
"name": "Jose Miguel"
},
{
"id": 5,
"name": "Beatriz"
},
{
"id": 6,
"name": "Rosario"
}
]
}
];
const clickedUser = 'Jose Miguel';
var userBooks = books
.filter(x => x.users.some(user => user.name.indexOf(clickedUser) > -1));
console.log(userBooks);

JS string counter

I want this code to count the number of times that category and message are in the bottom string, regardles of the different id number. The code just gives me the last one.
It is quite messy, sry. Thanks for your help.
Kind regards.
counter = function() {
var value = $('#text').val();
if (value.length == 0) {
$('#wordCount').html(0);
return;
}
var regex = /\s+/gi;
var wordCount = value.trim().replace(regex, ' ').split(' ').length;
};
$(document).ready(function() {
var obj = {"data" : {
"from": {
"category": "cat1",
"id": 1,
"message": "WIZ1"
},
"from": {
"category": "cat2",
"id": 2,
"message": "WIZ2"
},
"from": {
"category": "cat3",
"id": 3,
"message": "WIZ3"
},
"from": {
"category": "cat4",
"id": 4,
"message": "WIZ3"
},
}
};
$.each(obj, function() {
$.each(this, function(name, value) {
$("#result").append(name + '=' + value.category);
});
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="result">Words: <span id="wordCount">0</span>
<br/>
</div>
You have a single object with the "from" property being overwritten 4 times. It looks like what you wanted was to instead have an array, like the example below.
I also changed the code to actually capture the categories and messages, like the description in your post, after the loops those objects will have the count by message and count by category.
counter = function() {
var value = $('#text').val();
if (value.length == 0) {
$('#wordCount').html(0);
return;
}
var regex = /\s+/gi;
var wordCount = value.trim().replace(regex, ' ').split(' ').length;
};
$(document).ready(function() {
var obj = {"data" : [
{
"category": "cat1",
"id": 1,
"message": "WIZ1"
},
{
"category": "cat2",
"id": 2,
"message": "WIZ2"
},
{
"category": "cat3",
"id": 3,
"message": "WIZ3"
},
{
"category": "cat4",
"id": 4,
"message": "WIZ3"
},
]
};
var categories = {};
var messages = {}
$.each(obj, function() {
$.each(this, function(name, value) {
if (!categories[value.category]) categories[value.category] = 0;
categories[value.category] += 1;
if (!messages[value.message]) messages[value.message] = 0;
messages[value.message] += 1;
$("#result").append(name + '=' + value.category);
});
});
console.log(categories);
console.log(messages);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="result">Words: <span id="wordCount">0</span>
<br/>
</div>
Each of the items in the JS object have the same key ('from').
That's causing each one to overwrite the previous, which is why it's only finding the last one.
{
"data": {
"from1": {
"category": "cat1",
"id": 1,
"message": "WIZ1"
},
"from2": {
"category": "cat2",
"id": 2,
"message": "WIZ2"
},
"from3": {
"category": "cat3",
"id": 3,
"message": "WIZ3"
},
"from4": {
"category": "cat4",
"id": 4,
"message": "WIZ3"
}
}
}
This will allow it to iterate through each item as there will now be four items present.

Categories