JSON.stringify throws error - javascript

var Products = [
{ id: 0, product: 'Sour Apple', price: 10, count: 1, product_thumb: 'resources/css/apple.png' },
{ id: 1, product: '30 dsfdf', price: 20, count: 1, product_thumb: 'resources/css/croissant.png' },
{ id: 2, product: 'Discount Coffee', price: 30, count: 1, product_thumb: 'resources/css/coffecup.png' },
{ id: 3, product: '30 Donut Combo', price: 40, count: 1, product_thumb: 'resources/css/donut.png' },
{ id: 4, product: 'Invisishield', price: 50, count: 1, product_thumb: 'resources/css/apple.png' },
{ id: 5, product: 'Pink Cupcake', price: 60, count: 1, product_thumb: 'resources/css/icecream.png' },
{ id: 6, product: 'Strawberry Cone', price: 70, count: 1, product_thumb: 'resources/css/softy.png' }
]
I am trying to encode the product array (above) to JSON string and I am getting the following error: TypeError: Converting circular structure to JSON
UPDATE (from comment):
What i am trying to do is, i declare a var product = []; and then as and when user add's product to cart i do: var productObject = { id: id, product: name, price: price, count: 1, product_thumb: img }; Once the user says done, i take the array and want to convert it to json and send it to my web service. The problem is when i do JSON.stringify it gives that error. product.push(productObject);

TypeError: Converting circular structure to JSON
This error occurs when you have a cycle in your object. For example :
var obj = {};
obj.a = {b:obj};
If you browse obj, you have that cycle : obj->a->b->obj->...
So, JSON.stringify(obj) raises an error.
That kind of error can also occur when you include a DOM Object (window, document...), since they or their children reference them(self).

What browser? What environment? Just plugged your data into Chrome Inspector, works fine..

Related

Add/Sum multiple values of array and display using Javascript/Python

var input = [{id: 1, price: 1200, profit:60, name:'Messi'},
{id: 2, price: 600, profit:40, name:'Ronaldo'},
{id: 1, price: 100, profit:40, name:'Messi'},
{id: 1, price: 200, profit:30, name:'Messi'},
{id: 2, price: 400, profit:10, name:'Ronaldo'},
{id: 1, price: 800, profit:10, name:'Messi'}];
Expected Output:
[{id:1, name:'Messi', price:'2300', profit:'140'},
{id:2, name:'Ronaldo', price:'1000', profit:'50'},
]
Tried:
var output = { };
input.forEach(e => output[e.id] = (output[e.id] || 0) + e.price);
console.log(output);
How to make like the expected output here.
You can do it with the .reduce() method
var input = [{
id: 1,
price: 1200,
profit: 60,
name: 'Messi'
},
{
id: 2,
price: 600,
profit: 40,
name: 'Ronaldo'
},
{
id: 1,
price: 100,
profit: 40,
name: 'Messi'
},
{
id: 1,
price: 200,
profit: 30,
name: 'Messi'
},
{
id: 2,
price: 400,
profit: 10,
name: 'Ronaldo'
},
{
id: 1,
price: 800,
profit: 10,
name: 'Messi'
}
];
/* [{id:1, name:'Messi', price:'2300', profit:'140'},
{id:2, name:'Ronaldo', price:'1000', profit:'50'}] */
var result = []; //Initialize array
//array reduce
input.reduce(function(res, value) {
if (!res[value.name]) {
res[value.name] = {
id: value.id,
name: value.name,
price: 0,
profit: 0
};
result.push(res[value.name])
}
res[value.name].price += value.price; //sums price key values
res[value.name].profit += value.profit; //sums profit key values
return res; //returns response
}, {});
//output
console.log(result)
You can use Array.prototype.reduce() combined with Nullish coalescing assignment (??=)
Code:
const input = [{ id: 1, price: 1200, profit: 60, name: 'Messi' },{ id: 2, price: 600, profit: 40, name: 'Ronaldo' },{ id: 1, price: 100, profit: 40, name: 'Messi' },{ id: 1, price: 200, profit: 30, name: 'Messi' },{ id: 2, price: 400, profit: 10, name: 'Ronaldo' },{ id: 1, price: 800, profit: 10, name: 'Messi' },]
const result = input.reduce((a, c) => {
a[c.id] ??= { id: c.id, name: c.name, price: 0, profit: 0 }
a[c.id].price += c.price
a[c.id].profit += c.profit
return a
}, {})
console.log(Object.values(result))
There are two key things here.
You need to loop over the array of objects.
JavaScript provides several mechanisms for looping over arrays. You can use a traditional for statement. Or a for/of statement, or perhaps reduce as mentioned in the other answers.
You need to be able to group information by the name provided in the objects.
Objects are very useful here as they allow you to associate (read: "group") values with unique keys.
So, the general procedure is:
Initialise an object to use for storing the keys (names) and values (some more objects)
Loop over the input array. Take the name from the object and check to see if it exists as a key in the object. If it doesn't exist add it as a key, and then assign an initial object in the iteration as its value.
Update the values of that object where appropriate
Well, now you have an object of objects where what you want is an array of objects again, similar to your input. Use Object.values to return an array of the object's values (the nested objects).
Note: in your question your required output has the price and profit as strings rather than numbers so you may have to do an additional mapping operation on the array from Object.values to get that result. I've included that code at the end of the example along with some links to documentation of other code mentioned.)
In this example I'll use a for/of loop.
const input=[{id:1,price:1200,profit:60,name:"Messi"},{id:2,price:600,profit:40,name:"Ronaldo"},{id:1,price:100,profit:40,name:"Messi"},{id:1,price:200,profit:30,name:"Messi"},{id:2,price:400,profit:10,name:"Ronaldo"},{id:1,price:800,profit:10,name:"Messi"}];
// Initialise an empty object
const temp = {};
// For every object in the input array...
for (const obj of input) {
// Destructure the properties from it
const { id, price, profit, name } = obj;
// If the name doesn't exist as a key on the object
// add it, and assign an initial object to it that mirrors
// the current object in the iteration, but where the
// values of the properties that you want to increase are
// set to zero. The key is there just go to the next step
temp[name] ??= { id, name, price: 0, profit: 0 };
// Increase the price and profit values in
// the initialised object
temp[name].price += price;
temp[name].profit += profit;
}
// Finally, after the iteration, we return
// an array of those nested objects we've created
const output = Object.values(temp);
console.log(output);
// If you want to strings for those values
// you'll have to do an additional `map` to
// stringify them
const stringified = output.map(obj => {
// Use destructuring to get the profit and
// price properties, and assign everything else to `rest`
const { price, profit, ...rest } = obj;
// Return a new object by spreading out `rest`,
// and coercing the numbers to strings
return {
...rest,
price: price.toString(),
profit: profit.toString()
};
});
console.log(stringified);
Additional information
Destructuring assignment
Nullish coalescing assignment
Rest parameters
Spread syntax
Instead of computing just price, you can also compute profit and also add id and name, the result of each id being an object instead of a number. Then use Object.values() to get the final result. As has been demonstrated elsewhere Array#reduce can also be used to give us the intermediate result.
const input = [{id: 1, price: 1200, profit:60, name:'Messi'},
{id: 2, price: 600, profit:40, name:'Ronaldo'},
{id: 1, price: 100, profit:40, name:'Messi'},
{id: 1, price: 200, profit:30, name:'Messi'},
{id: 2, price: 400, profit:10, name:'Ronaldo'},
{id: 1, price: 800, profit:10, name:'Messi'}];
/*Expected Output:
[{id:1, name:'Messi', price:'2300', profit:'140'},
{id:2, name:'Ronaldo', price:'1000', profit:'50'},
]
Tried:*/
const output = { };
input.forEach(
e => output[e.id] = {
id: e.id,
name: e.name,
price:(output[e.id]?.price || 0) + e.price,
profit:(output[e.id]?.profit || 0) + e.profit
});
console.log(Object.values(output));
Give this a shot :)
var input = [{id: 1, price: 1200, profit:60, name:'Messi'},
{id: 2, price: 600, profit:40, name:'Ronaldo'},
{id: 1, price: 100, profit:40, name:'Messi'},
{id: 1, price: 200, profit:30, name:'Messi'},
{id: 2, price: 400, profit:10, name:'Ronaldo'},
{id: 1, price: 800, profit:10, name:'Messi'}];
function transform(input) {
let output = []
let lookup = {}
for (let i = 0; i < input.length; i++) {
let item = input[i]
let key = item.id
if (lookup[key]) {
lookup[key].price += item.price
lookup[key].profit += item.profit
} else {
lookup[key] = { ...item }
}
}
for (let key in lookup) {
output.push(lookup[key])
}
return output
}
console.log(transform(input))

Typescript- object with both dynamic and pre-defined keys, but with different types as values (or: object with a definition to the "rest-of-the-keys")

I'm converting a project from JS to TS, and a little stuck on the following object:
let campaigns = {
TOTAL: {
cost: 3,
revenue: 6,
year: 2020,
},
campaignId_1: {
name: "campaignName1",
cost: 1,
revenue: 4,
video: "video1",
},
campaignId_2: {
name: "campaignName2",
cast: 2,
revenue: 2,
video: "video2",
}
}
In Typescript, I want the object to ALWAYS have the "TOTAL" key with type-A value, and the rest of the keys are dynamic, and should have type-B value.
So first I've defined the child types:
type type_A = {
cost: number,
revenue: number,
year: number,
}
type type_B = {
name: string,
cast: number,
revenue: number,
video: string,
}
And now I'm trying to create a type for the parent Campaigns object:
Try 1:
type ICampaigns = {
TOTAL: type_A,
[key: string]: type_B, // Hoping it will be handled as "default" or "for all other non-defined keys"
}
But got the following error message: Property 'TOTAL' of type 'type_A' is not assignable to string index type 'type_B'.(2411).
Try 2:
type ICampaigns = {
TOTAL: type_A,
} & {
[key: string]: type_B, // Hoping it will be handled as "default" or "for all other non-defined keys"
}
And now it compiles, but when trying to define such instance I'm getting errors no matter if TOTAL is of type_A or type_B.
let campaigns: ICampaigns = {
TOTAL: {
cost: 3,
revenue: 6,
year: 2020,
},
campaignId_1: {
name: "campaignName1",
cost: 1,
revenue: 4,
video: "video1",
},
campaignId_2: {
name: "campaignName2",
cost: 2,
revenue: 2,
video: "video2",
}
}
I know that doing type_A|type_B will solve my problem, but then the TOTAL key will be able to have the type_B object as value, and other dynamic keys can have type_A values, which is not good.
I also know I can convert the Campaigns object to have only defined keys:
let campaigns: ICampaigns = {
TOTAL: {
cost: 3,
revenue: 6,
year: 2020,
},
campaignDetails: { // <--- change
campaignId_1: {
name: "campaignName1",
cost: 1,
revenue: 4,
video: "video1",
},
campaignId_2: {
name: "campaignName2",
cost: 2,
revenue: 2,
video: "video2",
}
}
}
But that's a lot of converting to do in my project, and also feels like giving up...
This happens because TOTAL also matches the definition of [key: string]: type_B, but this solution should work for you

Max element in an array in dailogflow

I am trying to calculate max element in an array . I tried this code but it is returning [object Object]
Is there something i am missing while doing in dailogflow.
function studentgroup(agent){
let games = [
{ id: 1, name: 'Star Wars: Imperial Assault', votes: 3},
{ id: 2, name: 'Game of Thrones: Second Edition', votes: 4 },
{ id: 3, name: 'Merchans and Marauders', votes: 5 },
{ id: 4, name: 'Eclipse', votes: 6 },
{ id: 5, name: 'Fure of Dracula', votes: 2 }
];
let maxGame = games.reduce((max, game) => max.votes > game.votes ? max : game);
agent.add(`${maxGame}`);
}
You can simply find the maximum element by iterating over the array.
let games = [
{ id: 1, name: 'Star Wars: Imperial Assault', votes: 3},
{ id: 2, name: 'Game of Thrones: Second Edition', votes: 4 },
{ id: 3, name: 'Merchans and Marauders', votes: 5 },
{ id: 4, name: 'Eclipse', votes: 6 },
{ id: 5, name: 'Fure of Dracula', votes: 2 }
];
maxElement = -Infinity;
element = null
for (const game of games) {
if (game.votes > maxElement) {
maxElement = game.votes;
element = game;
}
}
console.log(element)
The issue is that maxGame is an object. Using your example, that object will be
{ id: 4, name: 'Eclipse', votes: 6 }
But agent.add() is expecting to send back a string. The default "string" form of an object is "[object Object]", as you've seen.
You probably want to return something that makes more sense when displayed or read aloud, so it might make more sense for that line to be something like
agent.add(`The winner, with ${maxElement.votes} votes, is ${maxElement.name}.`)
which, given the example, would say something like
The winner, with 6 votes, is Eclipse.

Re-arrange a JSON object

I have data that comes from the database looking like:
[
{
ID: 1,
UPC: 11111,
Qty: 1,
Price: 1.99
},
{
ID: 2,
UPC: 11111,
Qty: 2,
Price: 1.99
},
{
ID: 3,
UPC: 22222,
Qty: 1,
Price: 9.99
},
{
ID: 4,
UPC: 11111,
Qty: 3,
Price: 1.99
},
{
ID: 5,
UPC: 22222,
Qty: 9,
Price: 9.99
}
]
Within the page, if they click a button, I need to rearrange it to look like this:
[
{
UPC: 11111,
Qty: 6,
Price: 1.99
},
{
UPC: 22222,
Qty: 10,
Price: 9.99
}
]
How can I convert this? 99% of the time I need the original dataset, so initially returning it like the 2nd dataset is not an option.
TIA.
Using the link posted above in the comments, I managed to make this work. I feel like there is probably a more efficient way using Underscore.js, but since I can't figure it out, this will do.
var tmpItems = {};
for (var i = items.length; i--;)
{
// init an array, if it is not there
tmpItems[it.All[i]['UPC']] = tmpItems[it.All[i]['UPC']] || [];
tmpItems[it.All[i]['UPC']]['Qty'] = (tmpItems[it.All[i]['UPC']]['Qty'] || 0) + (it.All[i]['Qty'] || 0);
tmpItems[it.All[i]['UPC']]['Price'] = (it.All[i]['Price'] || 0);
}
// convert back to an object
var newItems = [];
for (var key in tmpItems)
{
if (tmpItems.hasOwnProperty(key))
{
newItems.push({ 'UPC': key, 'Qty': tmpItems[key]['Qty'], 'Price': tmpItems[key]['Price'] });
}
}

Twitter bootstrap - typeahead

i use Twitter Bootstrap and typeahead.
i download the plugin: Twitter Bootstrap Typeahead Plugin Extension
and so long so good, its working when i use a static javascript json array eg.
$('#demo1').typeahead({
source: [
{ id: 9000, name: 'Aalborg' },
{ id: 2, name: 'Montreal' },
{ id: 3, name: 'New York' },
{ id: 4, name: 'Buffalo' },
{ id: 5, name: 'Boston' },
{ id: 6, name: 'Columbus' },
{ id: 7, name: 'Dallas' },
{ id: 8, name: 'Vancouver' },
{ id: 9, name: 'Seattle' },
{ id: 10, name: 'Los Angeles' }
],
itemSelected: displayResult
});
When i try to use ajax its will do enerything, my code look like this.
$('.typeahead').typeahead({
ajax: '/actions/search/synonymSearch',
itemSelected: displayResult
});
and its return this json array ( i can rebuild its on all ways, and i can't get it to work )
[
{ id: 1, name: 'Toronto' },
{ id: 2, name: 'Montreal' },
{ id: 3, name: 'New York' },
{ id: 4, name: 'Buffalo' },
{ id: 5, name: 'Boston' },
{ id: 6, name: 'Columbus' },
{ id: 7, name: 'Dallas' },
{ id: 8, name: 'Vancouver' },
{ id: 9, name: 'Seattle' },
{ id: 10, name: 'Los Angeles' }
]
The plugin home pages.
https://github.com/tcrosen/twitter-bootstrap-typeahead
i hobe enybardy can help me here :)
thanks a lot for all helping, :)
EDIT - Problem resovle! :)
Just need to added header("content-type: application/json");
into my PHP file, hobe this answere are usefull for orther peopole! :)
I don't think typeahead can read the source of your array and therefore I would parse it first.
var data = [{"id":"9000","name":"Aalborg"},{"id":"9220","name":null},{"id":"9210","name":null},{"id":"9200","name":"Aalborg SV"}];
// using underscore.js get an array of the city names
var cities = _.pluck(data, "name");
// now start typeahead
$el.typeahead(
source: cities,
// the value of the `city` selected gets passed to the onselect
onselect: function(city) {
// get the index of city selected from the data array
var i = _.indexOf(data, city);
// then using the index get what ever other value you need from the array
// and insert in the DOM etc..
}
);
This problem you are experiencing may be due to the page that you are calling via ajax isn't returning the right headers.
If you are using PHP try adding header('Content-type: application/json'); to the document that contains the JSON array.

Categories

Resources