I'm trying to form a list from json data, see the example What I want is that it will show me the value just once when duplicate values occurs (2x Martini glass, I want it to return just one in the list), but leaves the array as is, i.e. still want to be able to hold all values in the array.
There'd sure be a simple way to achieve this, but i'm not finding it...
var data = {
"cocktails": [{
"name": "Bloody Mary",
"glass": "longdrink",
"ingredients": {
"main": "vodka",
"secondary": "tomato juice",
"addition": "tabasco"
}
}, {
"name": "Daiquiri",
"glass": "martini glass",
"ingredients": {
"main": "white rum",
"secondary": "lime juice",
"addition": "sugar syrup"
}
}, {
"name": "Martini",
"glass": "martini glass",
"ingredients": {
"main": "gin",
"secondary": "vermout",
"addition": "olive"
}
}]
}
$(data.cocktails).each(function () {
var output = "<ul><li>" + this.glass + "</li></ul>";
$('#placeholder').append(output);
});
Create an empty array:
var glasses = [];
Push all the glasses to it.
data.cocktails.forEach(function (el) {
glasses.push(el.glass);
});
Create a deduped array of glasses. This leaves the glasses array intact so you can use it again.
var dedupedGlasses = glasses.filter(function(elem, pos) {
return glasses.indexOf(elem) == pos;
});
Use join to create the HTML list.
var html = '<ul><li>' + dedupedGlasses.join('</li><li>') + '</li></ul>';
And add to the page.
$('#placeholder').append(html);
Demo
The you can use something like this to grab the count for each glasses type:
function getCount(arr) {
return arr.reduce(function(m, e){
m[e] = (+m[e]||0)+1; return m
}, {});
}
console.log(getCount(glasses)); // { longdrink=1, martini glass=2 }
Basically the same as #Andy, though slightly different.
var data = {
"cocktails": [{
"name": "Bloody Mary",
"glass": "longdrink",
"ingredients": {
"main": "vodka",
"secondary": "tomato juice",
"addition": "tabasco"
}
}, {
"name": "Daiquiri",
"glass": "martini glass",
"ingredients": {
"main": "white rum",
"secondary": "lime juice",
"addition": "sugar syrup"
}
}, {
"name": "Martini",
"glass": "martini glass",
"ingredients": {
"main": "gin",
"secondary": "vermout",
"addition": "olive"
}
}]
}
$('#placeholder').append('<ul><li>' + data.cocktails.map(function (cocktail) {
return cocktail.glass;
}).filter(function (glass, index, array) {
return array.indexOf(glass) === index;
}).join('</li><li>') + '</li></ul>');
On jsFiddle
I can just point to a solution where you need to define a new array
var cleanArr = []
$(data.cocktails).each(function () {
if($.inArray(this.glass, cleanArr) === -1) cleanArr.push(this.glass);
var output = "<ul><li>" + this.glass + "</li></ul>";
$('#placeholder').append(output);
});
console.log(cleanArr)
I like the way the array is created, that's why I'm posting this solution but it is not my idea --> taken from:
Remove Duplicates from JavaScript Array
Here is a working jsFiddle for you.
http://jsfiddle.net/Q74pj/1/
And here is what I did:
I used a new function calling uniqueArray to create a new associative array (which would be the quickest to iterate and fill).
You need to create a unique array, or sort it by drink and skip the duplicates.
function uniqueValues (arr) {
//if (arr.length < 2) return arr;
var retVal = {};
for (var i = 0; i < arr.length; i++) {
retVal[arr[i].glass] = arr[i];
}
return retVal;
}
then just use another way to iterate the associate array:
var uniqueArrayValues = uniqueValues(data.cocktails);
for (key in uniqueArrayValues) {
var output = "<ul><li>" + uniqueArrayValues[key].glass + "</li></ul>";
$('#placeholder').append(output);
}
Related
So, I have data. It is array of objects.
data = [
{
"id": "200",
"price": "5000"
},
{
"id": "137",
"price": "8000"
},
{
"id": "230",
"price": "9000"
},
{
"id": "241",
"price": "9000"
},
{
"id": "78",
"price": "10000"
}
]
json=JSON.parse(data);
I make something like pager.
My code should return nearby (previous and next) elements of original element.
It is not allowed to change order of objects.
I'm trying to do something like
json.indexOf(JSON.parse('{"id":"200","price":"5000"}'))
but it returns -1.
Also json[0]==JSON.parse('{"id":"200","price":"5000"}') return false, but I think that this elements are similar.
What way do you see?
json=JSON.parse('[{"id":"200","price":"5000"},{"id":"137","price":"8000"},{"id":"230","price":"9000"},{"id":"241","price":"9000"},{"id":"78","price":"10000"}]');
console.log(json.indexOf(JSON.parse('{"id":"200","price":"5000"}')));
console.log(json[0]==JSON.parse('{"id":"200","price":"5000"}'));
console.log(json[0]);
console.log(JSON.parse('{"id":"200","price":"5000"}'));
You could take a function which finds the index of the wanted id and returns items before that index, the index and one after the index with adjustment at the beginning of the array.
function getParts(id) {
var index = array.findIndex(o => o.id === id),
min = Math.max(index - 1, 0);
if (index !== -1) {
return array.slice(min, min + (index ? 3 : 2));
}
}
var array = JSON.parse('[{"id":"200","price":"5000"},{"id":"137","price":"8000"},{"id":"230","price":"9000"},{"id":"241","price":"9000"},{"id":"78","price":"10000"}]');
console.log(getParts('200'));
console.log(getParts('137'));
console.log(getParts('230'));
console.log(getParts('78'));
Try this, i think it would work as the 'id' property is unique
var words = [{"id":"200","price":"5000"},{"id":"137","price":"8000"},{"id":"230","price":"9000"},{"id":"241","price":"9000"},{"id":"78","price":"10000"}];
let k;
let sayYourKeyId = "137";
const result = words.find((word, index) => {
if(word.id == sayYourKeyId){
k = index;
}
});
console.log(words[k-1]);
console.log(words[k]);
console.log(words[k+1]);
I've got an array of three people. I want to add a new key to multiple objects at once based on an array of indices. Clearly my attempt at using multiple indices doesn't work but I can't seem to find the correct approach.
var array = [
{
"name": "Tom",
},
{
"name": "Dick",
},
{
"name": "Harry",
}
];
array[0,1].title = "Manager";
array[2].title = "Staff";
console.log(array);
Which returns this:
[
{
"name": "Tom",
},
{
"name": "Dick",
"title": "Manager"
},
{
"name": "Harry",
"title": "Staff"
}
]
But I'd like it to return this.
[
{
"name": "Tom",
"title": "Manager"
},
{
"name": "Dick",
"title": "Manager"
},
{
"name": "Harry",
"title": "Staff"
}
]
You cannot use multiple keys by using any separator in arrays.
Wrong: array[x, y]
Correct: array[x] and array[y]
In your case, it will be array[0].title = array[1].title = "manager";
1st method::
array[0].title = "Manager";
array[1].title = "Manager";
array[2].title = "Staff";
array[0,1] will not work.
2nd method::
for(var i=0;i<array.length;i++) {
var msg = "Manager";
if(i===2) {
msg = "Staff"
}
array[i].title = msg
}
You can use a helper function like this
function setMultiple(array, key, indexes, value)
{
for(i in array.length)
{
if(indexes.indexOf(i)>=0){
array[i][key] = value;
}
}
}
And then
setMultiple(array, "title", [0,1], "Manager");
Try this: `
for (var i=0; var<= array.length; i++){
array[i].title = "manager";
}`
Or you can change it around so var is less than or equal to any n range of keys in the index.
EDIT: instead make var <= 1. The point is to make for loops for the range of indices you want to change the title to.
Assuming that you have a bigger set of array objects.
var array = [
{
"name": "Tom",
},
{
"name": "Dick",
},
{
"name": "Harry",
},
.
.
.
];
Create an object for the new keys you want to add like so:
let newKeys = {
'Manager': [0,2],
'Staff': [1]
}
Now you can add more such titles here with the required indexes.
with that, you can do something like:
function addCustomProperty(array, newKeys, newProp) {
for (let key in newKeys) {
array.forEach((el, index) => {
if (key.indexOf(index) > -1) { // if the array corresponding to
el[newProp] = key // the key has the current array object
} // index, then add the key to the
}) // object.
}
return array
}
let someVar = addCustomProperty(array, newKeys, 'title')
Assuming I have a fairly complex JSON object such as the one below:
{
"firstName": "John",
"address": [{
"streetAddress": "1 street",
"special": {
"1 a": "1 b"
}
}, {
"streetAddress": "2 naist street",
"special": {
"2 a": "2 b"
}
}],
"phoneNumbers": [{
"type": "iPhone",
"number": "0123-4567-8888"
}]
}
Are there any tools/libraries to calculate the JSONPath to a particular key/value pair?
For example: the JSONPath to the third row of data (streetAddress": "1 street") can be extracted using the JSONPath $.address[0].streetAddress.
Ideally I want something like this: calculateJSONPath(3) - where 3 is the 3rd key starting from the top and this function will return $.address[0].streetAddress. I don't imagine there'll be something that does exactly this, but I got to start somewhere, if not from scratch.
Edit: Sample use case: User loads a JSON file into my application. I allow them to select specific keys using my interface. Imagine checkboxes next to each key. Next the user can upload a second JSON file and see if the previously selected keys/values are present in the second JSON.
Edit 2: Searching won't work as I want the key/values selected by the user to be located in the same hierarchy as the first JSON. Eg: If the user selects "1 a": "1 b" in the first JSON file, the second JSON file must have the same key/value in the same nested hierarchy.
Here is a taste of what I said (check an checkbox to see my point):
var $ = {
"firstName": "John",
"address": [{
"streetAddress": "1 street",
"special": {
"2 a": "2 a"
}
}, {
"streetAddress": "2 naist street",
"special": {
"2 a": "2 a"
}
}],
"phoneNumbers": [{
"type": "iPhone",
"number": "0123-4567-8888"
}]
};
// the recursive function that create the structure
function createStructure(container, obj, path) {
for(var key in obj) {
var d = cd(key, path);
if(typeof obj[key] == "object")
createStructure(d, obj[key], path + "['" + key + "']");
container.appendChild(d);
}
}
// of course we call the function ...
createStructure(document.getElementById("preview"), $, "$");
// create a preview element (unimportant)
function cd(prop, path) {
var d = document.createElement("div");
var s = document.createElement("span");
var i = document.createElement("input");
i.setAttribute("data-path", path + "['" + prop +"']");
i.onclick = check;
i.type = "checkbox";
s.appendChild(i);
s.appendChild(document.createTextNode(prop));
d.appendChild(s);
return d;
}
// the event handler (unimportant)
function check(e) {
if(e.target.checked)
alert(e.target.getAttribute("data-path"));
}
div {
padding: 5px;
padding-left: 25px;
}
span {
border-bottom: 1px solid black;
border-left: 1px solid black;
}
<div id="preview">
<span><input type="checkbox" data-path="$"/>$</span>
</div>
The following will create an DOM-structure from the supplied JSON. It's very rudimentary and it's just a proof of concept.
You will need to build the rest of the application from this foundation.
Every level (UL) has an index and every item (LI) a key.
//rework JSON into a structure
function buildDOMFromJSON()
{
var JSONString = document.querySelector("#uploadedJSON1").value;
//you need to add a gazillion error checks
var JSONcompiled = JSON.parse(JSONString);
//test if array or object
var start, isObject;
if (JSONcompiled instanceof Array)
{
start = JSONcompiled;
isObject = false;
}
else //is object
{
start = Object.keys(JSONcompiled); //create an array to start
isObject = JSONcompiled;
}
loopJSONLevel(start, isObject, document.querySelector("#JSONstructure1 > ul"));
}
function loopJSONLevel(obj, isObject, level)
{
//loop the array
obj.forEach(function(element, index){
var objectToEvaluate;
if (isObject == false)
{
objectToEvaluate = element;
}
else
{
objectToEvaluate = isObject[element];
}
//create a new level
var newLevel = document.createElement("li");
var newLevel2 = document.createElement("ul");
if (objectToEvaluate instanceof Array)
{
//create a new level
if (element instanceof Array)
{
newLevel2.setAttribute("data-path", index);
}
else
{
newLevel2.setAttribute("data-path", element);
}
level.appendChild(newLevel2);
loopJSONLevel(objectToEvaluate, false, newLevel2);
}
else if (Object.prototype.toString.call(objectToEvaluate) == "[object Object]")
{
//create a new level
if (element instanceof Array)
{
newLevel2.setAttribute("data-path", index);
}
else
{
newLevel2.setAttribute("data-path", element);
}
newLevel2.setAttribute("data-path", index);
level.appendChild(newLevel2);
loopJSONLevel(Object.keys(objectToEvaluate), objectToEvaluate, newLevel2);
}
else
{
//draw the value
level.appendChild(newLevel);
newLevel.textContent = objectToEvaluate;
newLevel.setAttribute("data-path", element);
}
});
}
buildDOMFromJSON()
<textarea id="uploadedJSON1">
[{
"firstName": "John",
"address": [{
"streetAddress": "1 street",
"special": {
"1 a": "1 b"
}
}, {
"streetAddress": "2 naist street",
"special": {
"2 a": "2 b"
}
}],
"phoneNumbers": [{
"type": "iPhone",
"number": "0123-4567-8888"
}]
}]
</textarea>
<div id="JSONstructure1">
<ul>
</ul>
</div>
I have done the same thing using Angular.
You can see an example below :
https://davicvictor.github.io/jsonpath-ui/ and open your console.
I am trying to parse and show JSON data (product catalog) using XMLHttpRequest method. I am able to display the brands and their names, but not able to showcase list of products progmatically.
Here is the sample JSON request:
{
"products": {
"laptop": [{
"brand": "sony",
"price": "$1000"
}, {
"brand": "acer",
"price": "$400"
}],
"cellphone": [{
"brand": "iphone",
"price": "$800"
}, {
"brand": "htc",
"price": "$500"
}],
"tablets": [{
"brand": "iPad",
"price": "$800"
}, {
"brand": "htc-tab",
"price": "$500"
}]
}
}
Right now I am using following code to show data in tabluar form:
function loadJSON() {
var data_file = "http://localhost/AJAX/productcatalog.json";
var http_request = new XMLHttpRequest();
http_request.onreadystatechange = function () {
if ((http_request.readyState == 4) && (http_request.status == 200)) {
// Javascript function JSON.parse to parse JSON data
var jsonObj = JSON.parse(http_request.responseText);
data = '<table border="2"><tr><td>Type</td><td>Brand</td><td>Price</td></tr>';
var i = 0;
debugger;
for (i = 0; i < jsonObj["products"].laptop.length; i++)
{
obj = jsonObj["products"].laptop[i];
data = data + '<tr><td>laptop</td><td>' + obj.brand + '</td><td>' + obj.price + '</td></tr>';
}
for (i = 0; i < jsonObj["products"].cellphone.length; i++)
{
obj = jsonObj["products"].cellphone[i];
data = data + '<tr><td>laptop</td><td>' + obj.brand + '</td><td>' + obj.price + '</td></tr>';
}
for (i = 0; i < jsonObj["products"].tablets.length; i++)
{
obj = jsonObj["products"].tablets[i];
data = data + '<tr><td>laptop</td><td>' + obj.brand + '</td><td>' + obj.price + '</td></tr>';
}
data += '</table>';
document.getElementById("demo").innerHTML = data;
}
}
http_request.open("GET", data_file, true);
http_request.send();
}
Question What is the way to fetch product list , i.e. products, cellphone and tablets ? Right now I have hardcoded that in order to fetch complete list of brands. Please advice. (I want to use plain javascript and not jquery)
Thanks!
It sounds like what you're missing is the "How do I iterate over an object when I don't know all the keys".
An object is a set of key, value pairs. You can use for/in syntax: for( var <key> in <object> ){} to get each key.
For your use case it might be something like:
var products = jsonObject['products'];
for( var productName in products ){
//productName would be "laptop", "cellphone", etc.
//products[productName] would be an array of brand/price objects
var product = products[productName];
for( var i=0; i<product.length; i++ ){
//product[i].brand
//product[i].price
}
}
In practice, I might use something a little less verbose, but this makes it easier to understand what is going on.
To achieve the expected i have used for loop and HTML DOM createElement() Method
var product_catalog = {
"products": {
"laptop": [{
"brand": "sony",
"price": "$1000"
}, {
"brand": "acer",
"price": "$400"
}],
"cellphone": [{
"brand": "iphone",
"price": "$800"
}, {
"brand": "htc",
"price": "$500"
}],
"tablets": [{
"brand": "iPad",
"price": "$800"
}, {
"brand": "htc-tab",
"price": "$500"
}]
}
};
var output = document.querySelector('#product tbody');
function build(JSONObject) {
/**get all keys***/
var keys = Object.keys(JSONObject);
/**get all subkeys***/
var subkeys = Object.keys(JSONObject[keys]);
console.log(subkeys);
/**loop sub keys to build HTML***/
for (var i = 0, tr, td; i < subkeys.length; i++) {
tr = document.createElement('tr');
td = document.createElement('td');
td.appendChild(document.createTextNode(subkeys[i]));
tr.appendChild(td);
output.appendChild(tr);
}
};
build(product_catalog);
HTML:
Coepen URL for reference- http://codepen.io/nagasai/pen/xOOqMv
Hope this works for you :)
Look at this example:
var x = data.key1.children.key4;
var path = "data";
function search(path, obj, target) {
for (var k in obj) {
if (obj.hasOwnProperty(k))
if (obj[k] === target)
return path + "['" + k + "']"
else if (typeof obj[k] === "object") {
var result = search(path + "['" + k + "']", obj[k], target);
if (result)
return result;
}
}
return false;
}
//Then for evry node that you need you can call the search() function.
var path = search(path, data, x);
console.log(path); //data['key1']['children']['key4']
I think this is what you're asking about, you can use Object.keys to get the properties of an object, then loop through them afterward.
var data = {
"products": {
"laptop": [{
"brand": "sony",
"price": "$1000"
}, {
"brand": "acer",
"price": "$400"
}],
"cellphone": [{
"brand": "iphone",
"price": "$800"
}, {
"brand": "htc",
"price": "$500"
}],
"tablets": [{
"brand": "iPad",
"price": "$800"
}, {
"brand": "htc-tab",
"price": "$500"
}]
}
}
var typesOfProducts = Object.keys(data.products)
console.log(typesOfProducts)
document.getElementById('output').textContent = typesOfProducts.toString()
//Then, to loop through
var i = -1,
len = typesOfProducts.length
function handleProduct(productType) {
console.log("This is the " + productType + " data.")
console.log(data.products[productType])
}
while (++i < len) {
handleProduct(typesOfProducts[i])
}
<div id="output"></div>
It sounds like what you're looking for is just an array of the keys of the "products" object. Example:
Products: ["laptop", "cellphone", "tablets"];
If so, I would just run your json object through javascript's Object.keys() method.
var jsonObj = JSON.parse(http_request.responseText);
var products = Object.keys(jsonObj.products);
// products = ["laptop", "cellphone", "tablets"];
I need to convert a large CSV data set to JSON, however the output should be a JSON dictionary like this:
var products = {
"crystal": {
"description": "This is a crystal",
"price": "2.95"
},
"emerald": {
"description": "This is a emerald",
"price": "5.95"
}
};
This is what the CSV table would look like:
I am using a script referenced here to generate the JSON:
var csv = require('csv')
var fs = require('fs')
var f = fs.createReadStream('Fielding.csv')
var w = fs.createWriteStream('out.txt')
w.write('[');
csv()
.from.stream(f, {columns:true})
.transform(function(row, index) {
return (index === 0 ? '' : ',\n') + JSON.stringify(row);
})
.to.stream(w, {columns: true, end: false})
.on('end', function() {
w.write(']');
w.end();
});
However the output from that script is created in this format:
[
{
"name": "crystal",
"description": "This is a crystal",
"price": "2.95"
},
{
"name": "emerald",
"description": "This is a emerald",
"price": "5.95"
}
]
How would I modify the script to get my desired "dictionary" format?
All you need to do is loop over the array and use item.name as key for your dictionary object
var products ={};
data.forEach(function(item){
products[item.name] = item;
});
This will leave the name property in the item but that shouldn't be an issue
I found csv parser library most useful:
var csvText=`status,path,name,ext,checksum,size,document_service_id,document_service_path,message
success,./15-02-2017_17-11/d77c7886-ffe9-40f2-b2fe-e68410d07891//expE1.txt,expE1.txt,txt,38441337865069eabae7754b29bb43e1,414984,8269f7e3-3221-49bb-bb5a-5796cf208fd1,/neuroinftest/20170215/expE1.txt,
success,./15-02-2017_17-11/d77c7886-ffe9-40f2-b2fe-e68410d07891//expE10.txt,expE10.txt,txt,f27e46979035706eb0aaf58c26e09585,368573,2c94ed19-29c9-4660-83cf-c2148c3d6f61,/neuroinftest/20170215/expE10.txt,
success,./15-02-2017_17-11/d77c7886-ffe9-40f2-b2fe-e68410d07891//expE2.txt,expE2.txt,txt,e1040d9546423c823944120de0e5c46c,333308,b3898f5d-1058-4cf3-acf9-76759117b810,/neuroinftest/20170215/expE2.txt,
`
var csv = require('csv');
csv.parse(csvText, {columns: true}, function(err, data){
console.log(JSON.stringify(data, null, 2));
});
In variable csvText I have my comma-separated file, with the first line serving as a header. I use the parse function and I'm passing the {columns: true} to indicated that the first line has the headers. Second parameter in the callback function (data) has the object with keys being the headers and the values being the corresponding csv cells. I use JSON.stringify to print it nicely and the result object looks like this (it puts it into an array):
[
{
"status": "success",
"path": "./15-02-2017_17-11/d77c7886-ffe9-40f2-b2fe-e68410d07891//expE1.txt",
"name": "expE1.txt",
"ext": "txt",
"checksum": "38441337865069eabae7754b29bb43e1",
"size": "414984",
"document_service_id": "8269f7e3-3221-49bb-bb5a-5796cf208fd1",
"document_service_path": "/neuroinftest/20170215/expE1.txt",
"message": ""
},
{
"status": "success",
"path": "./15-02-2017_17-11/d77c7886-ffe9-40f2-b2fe-e68410d07891//expE10.txt",
"name": "expE10.txt",
"ext": "txt",
"checksum": "f27e46979035706eb0aaf58c26e09585",
"size": "368573",
"document_service_id": "2c94ed19-29c9-4660-83cf-c2148c3d6f61",
"document_service_path": "/neuroinftest/20170215/expE10.txt",
"message": ""
},
{
"status": "success",
"path": "./15-02-2017_17-11/d77c7886-ffe9-40f2-b2fe-e68410d07891//expE2.txt",
"name": "expE2.txt",
"ext": "txt",
"checksum": "e1040d9546423c823944120de0e5c46c",
"size": "333308",
"document_service_id": "b3898f5d-1058-4cf3-acf9-76759117b810",
"document_service_path": "/neuroinftest/20170215/expE2.txt",
"message": ""
}
]
UPD: This array can easily be turned into the object you need with reduce:
var res_obj = data.reduce(function(acc, cur, i) {
acc[cur.name] = cur;
return acc;
}, {});
In my case I use the name property as a key. Make sure it's unique.
I think something like this would work :
var products_arr = [{"name":"crystal","description":"This is a crystal","price":"2.95"},
{"name":"emerald","description":"This is a emerald","price":"5.95"}]
var products = {};
for (var i = 0, l = products_arr.length ; i < l ; ++i) {
var x = products_arr[i];
var name = x.name
delete x.name; // deletes name property from JSON object
products[name] = x;
}
This will output :
{
"crystal": {
"description": "This is a crystal",
"price": "2.95"
},
"emerald": {
"description": "This is a emerald",
"price": "5.95"
}
}
If you would like to modify your specific code, you could change the line
return (index === 0 ? '' : ',\n') + JSON.stringify(row);
to
var clonedRow = JSON.parse(JSON.stringify(row));
var key = clonedRow['name'];
delete clonedRow['name'];
var newRow = {};
newRow[key] = clonedRow;
return (index === 0 ? '' : ',\n') + JSON.stringify(newRow);
This creates a new object for each row, modifying the structure according to your requirement.
Your best bet is to use PapaParse, a powerful csv parser/dumper. It supports streams, various string encodings, header row, and it's fast.