I have an API endpoint that expects the following json string (this is an example).
{
"userid": "1234",
"bookshelf": "3",
"bookcount": "6",
"books":[
{"bookid": "1"},
{"bookid": "2"},
{"bookid": "3"},
{"bookid": "6"}
]}
The api accesses each in the following manner:
$userid = $data['userid'];
$bookshelf = $data['bookshelf'];
etc...
I can also loop through the books and get each bookid with:
$data['books'][$i]['bookid']
When I send the above json string via a tool like postman it works fine. I'm having trouble manufacturing this same json string on the javascript side. This is how I populate the data on the front end of my site.
var data = new Array();
data.push({ "userid": "1234" });
data.push({ "bookshelf": "3" });
data.push({ "bookcount": "6" });
data.push({ "books": selectedbooks }); // This is an array of bookid objects
The problem is whether I json.stringify it and send to the webserver and over to the api or have the webserver json_encode, I end up with numeric indexes that can only be accessed like $data[0]['userid']; once it's decoded by the api, which differs from how the api is working. This would require the json string to be assembled in an exact sequence if the api supported it.
How do I go about getting this data in the required format?
The numeric indexes are coming because you're preparing the data as an array, not an object. Arrays in JavaScript are indexed, not associative. Try:
var data = {
userid: 1234,
bookshelf: 3,
bookcount: 6,
books: selectedbooks
};
You need to create an object, not an array.
var data = {};
data["userid"] = "1234";
data["bookshelf"] = "3";
data["bookcount"] = "6";
data["books"] = selectedbooks; // This is an array of bookid objects
Your data is not supposed to be an array, it's an object. The only array is in the books property.
var data = {}
data.userid = "1234";
data.bookshelf = "3";
data.bookcount = "6";
data.books = selectedbooks;
or more succintly:
var data = {
userid: "1234",
bookshelf: "3",
bookcount: 6,
books: selectedbooks
};
You can easily transform your array into the expected object :
var arr = [
{"userid": "1234"},
{"bookshelf": "3"},
{"bookcount": "6"},
{
"books":[
{"bookid": "1"},
{"bookid": "2"},
{"bookid": "3"},
{"bookid": "6"}
]}
];
var obj = {} ;
arr.forEach(item => {
var key = Object.keys(item)[0];
obj[key] = item[key];
});
console.log(obj);
//{ userid: '1234',
// bookshelf: '3',
// bookcount: '6',
// books:
// [ { bookid: '1' },
// { bookid: '2' },
// { bookid: '3' },
// { bookid: '6' } ] }
Related
I am working on an angular app. I have an array "*identify duplicate" which is something like this.
[
{
Name: "Jack",
Id: "1"
},
{
Name: "Rose",
Id: "2"
},
{
Name: "Jack",
Id: "4"
},
{
Name: "Jack",
Id: "4"
}
]
If I get same name and Id I want to remove both. Like in above example in last two indexes name and Id are same "jack" But I don't want to delete any data if name is name and id is different. Like in above example I want to keep Jack with Id "1" in my array but want to delete last two "Jack" with Id "4". How can I do that?
You can do this with several methods like filter,reduce,map etc... I create two example below.
First one is simple forEach method. In a loop filter if there is same Name element then push to new array
In second reduce method again filter element if there is same Name element then push to new array.
var arr=[{Name: "Jack",Id: "1"},{Name: "Rose",Id: "2"},{Name: "Jack", Id: "4"},{Name: "Jack", Id: "4"}];
//simple foreach method
var result=[];
arr.forEach(el=>{
if(result.filter(x=>x.Name==el.Name).length==0){result.push(el);}
});
console.log(result);
//reduce method
var result2=arr.reduce((r, { Name, Id }) => {
var temp = r.find(o => Name === o.Name);
if (!temp) { r.push(temp = { Name, Id });}
return r;
}, []);
console.log(result2);
Using Map()
var arr = [{Name:"Jack",Id:"1"},{Name:"Rose",Id:"2"},{Name:"Jack",Id:"4"},{Name:"Jack",Id:"4"}]
let res = [...arr.reduce((a, i) => a.has(i.Name) ? a : a.set(i.Name, i), new Map).values()]
console.log(res)
Try this:
var a = [
{
"Name": "Jack",
"Id": "1"
},
{
"Name": "Jack",
"Id": "4"
},
{
"Name": "Jack",
"Id": "4"
}
]
var jsonObject = a.map(JSON.stringify);
console.log(jsonObject);
var uniqueSet = new Set(jsonObject);
var uniqueArray = Array.from(uniqueSet).map(JSON.parse);
console.log(uniqueArray);
If you can use Javascript libraries such as underscore or lodash, I recommend having a look at _.uniq function in their libraries. From lodash
var arr=[ { Name: "Jack", Id: "1" }, { Name: "Rose", Id: "2" }, { Name: "Jack", Id: "4" }, { Name: "Jack", Id: "4" } ]; var updated_data = _.uniq(arr, 'Name');
I have an array which has some objects and one of the propery of the object can have dupes viz. Account. Now i want to convert this array to map with key having Account's property value and the corresponding dupes should be stored as an array to that key in the map.
let arr = [];
arr.push({'Key':'1','Record':{'Account':'a','data':'A1'}});
arr.push({'Key':'2','Record':{'Account':'b','data':'123'}});
arr.push({'Key':'3','Record':{'Account':'a','data':'A2'}});
arr.push({'Key':'4','Record':{'Account':'a','data':'A3'}});
arr.push({'Key':'5','Record':{'Account':'c','data':'123'}});
const accIdMap= arr.reduce((map,obj) => (map[obj.Record.Account] = obj,map), {});
console.log(arr);
console.log(accIdMap);
So as of now the accIdMap just gets a one to one key-value mapping which is the last one pushed in the array i.e 4 but i want that the output map should have value as an array where ever the keys were duplicated. I tried reduction but that eliminates the duplicate values but i want the duplicate values as an corresponding array.
For example
As is output
{
"a": {
"Key": "4",
"Record": {
"Account": "a",
"data": "A3"
}
},
"b": {
"Key": "2",
"Record": {
"Account": "b",
"data": "123"
}
},
"c": {
"Key": "5",
"Record": {
"Account": "c",
"data": "123"
}
}
}
Desired OutPut (the keys which were duplicated should have the values added as an array)
{
"a": [{"Key": "4","Record": {"Account": "a","data": "A3"}},{
"Key": "3",
"Record": {
"Account": "a",
"data": "A2"
}
},{
"Key": "1",
"Record": {
"Account": "a",
"data": "A1"
}
}],
"b": {
"Key": "2",
"Record": {
"Account": "b",
"data": "123"
}
},
"c": {
"Key": "5",
"Record": {
"Account": "c",
"data": "123"
}
}
}
You can use reduce like this:
Check if the accumulator already has key with current a.Record.Account. If yes, push the current item in context to it. Else, add a.Record.Account as a key and then push the item to it.
const input = [{'Key':'1','Record':{'Account':'a','data':'A1'}},
{'Key':'2','Record':{'Account':'b','data':'123'}},
{'Key':'3','Record':{'Account':'a','data':'A2'}},
{'Key':'4','Record':{'Account':'a','data':'A3'}},
{'Key':'5','Record':{'Account':'c','data':'123'}}]
const output = input.reduce((acc, a) =>
((acc[a.Record.Account] = acc[a.Record.Account] || []).push(a), acc), {})
console.log(output);
Doing a check in the reduce function if the value exists already, then based on that you can do the following. If the Account already exists then check if the map has a array on that Account's key. If not create an array with the existing element and the current one by creating an empty array and pushing to that. If it is an array then just push to it. If the Account key doesn't exist then just set the value as the obj.
Update: Reordered the initialization of const m and added comment on code.
let arr = [];
arr.push({'Key':'1','Record':{'Account':'a','data':'A1'}});
arr.push({'Key':'2','Record':{'Account':'b','data':'123'}});
arr.push({'Key':'3','Record':{'Account':'a','data':'A2'}});
arr.push({'Key':'4','Record':{'Account':'a','data':'A3'}});
arr.push({'Key':'5','Record':{'Account':'c','data':'123'}});
const accIdMap= arr.reduce((map,obj) => {
if(map[obj.Record.Account]) { // the property exists and can be an array or the obj
if(!map[obj.Record.Account].length) { // means just the object. Creating an array then pushing the existing obj to it
const m = (map[obj.Record.Account]);
map[obj.Record.Account] = [];
map[obj.Record.Account].push(m);
}
map[obj.Record.Account].push(obj); // if it was an array this will push it to the existing array. If it wasn't the previous if have created and inserted old value and this line pushes to the new array
} else {
map[obj.Record.Account] = obj; // just putting the obj value as it wasn't a duplicate
}
return map;
}, {});
console.log(arr);
console.log(accIdMap);
This works like what you expected. take this result and match with your desired output.
let arr = [];
arr.push({ 'Key': '1', 'Record': { 'Account': 'a', 'data': 'A1' } });
arr.push({ 'Key': '2', 'Record': { 'Account': 'b', 'data': '123' } });
arr.push({ 'Key': '3', 'Record': { 'Account': 'a', 'data': 'A2' } });
arr.push({ 'Key': '4', 'Record': { 'Account': 'a', 'data': 'A3' } });
arr.push({ 'Key': '5', 'Record': { 'Account': 'c', 'data': '123' } });
var obj = {}
arr.map((e) => {
var filteredArr = arr.filter((f) => f.Record.Account == e.Record.Account)
if (filteredArr.length > 1)
obj[e.Record.Account] = filteredArr
else if (filteredArr.length != 0)
obj[e.Record.Account] = filteredArr[0]
})
console.log(JSON.stringify(obj))
I have a JSON file
{
"data": [
{
"name": "Jake",
"id": "123"
},
{
"name": "Bob",
"id": "234"
}]
}
with all id's unique, and say I have an array of banned ids ["123","423"] and I would like to delete all entries that have an id number in the array (so as an output I'd like the following).
{
"data": [
{
"name": "Bob",
"id": "234"
}]
}
What would be a moderately efficient way (runs in a few seconds on an ordinary computer) to achieve this if there's a few thousand entries in the JSON and array?
You can use the Array.prototype.filter() method in conjunction with .indexOf():
var bannedIds = ["123", "423"];
var input = {
"data": [
{
"name": "Jake",
"id": "123"
},
{
"name": "Bob",
"id": "234"
}]
};
input.data = input.data.filter(function(v) {
return bannedIds.indexOf(v.id) === -1;
});
console.log(input);
If you don't want to overwrite the original array then just assign the result of the .filter() call to a new variable.
If the above turns out to be too slow with your large amount of data, you can try replacing .filter() with a conventional for loop, and/or replacing .indexOf() with a lookup object created from the array of banned ids.
If you can use ES6, you can do this:
const source = {
"data": [
{
"name": "Jake",
"id": "123"
},
{
"name": "Bob",
"id": "234"
}
]
};
const banned = ["123", "423"];
// O(n) startup cost for constant access time later
const bannedSet = new Set(banned);
// O(n)
const result = source.data.filter(x => !bannedSet.has(x.id));
console.log(result);
As mentioned in the comments, there's a startup cost for creating the Set. However, this lets you then call Set.prototype.has, which is constant.
Then, it's just a matter of iterating over every element and filtering out the ones that are in the banned set.
If you can't use ES6, you could replace Set with a plain JS object. If you have to support IE<9, use a polyfill for Array.prototype.filter (thanks #nnnnnn).
UPDATE
#SpencerWieczorek points out that the ES6 spec seems to indicate that Set.prototype.has iterates. I spoke too soon about the lookup being constant (I was carrying over my experience from other languages). Typically, sets will do better than O(n), e.g. constant or O(log n) depending on the underlying implementation. Your mileage may vary, so nnnnnn's answer may be faster in some cases.
Try a few of the solutions here with large amounts of data to confirm.
EDIT
I shied away from using filter or the like because that involves creating a new array. That's actually probably fine for the data sizes we're talking about, but the approach I have below is more efficient.
On my laptop, this whole program runs in about 0.2 seconds. (It uses 10,000 entries and 100 banned IDs.)
var o = {
data: []
};
for (var i = 0; i < 10000; i++) {
o.data.push({
name: i % 2 === 0 ? 'Jake' : 'Bob', // couldn't think of more names :-)
id: ''+i // convert to string
});
}
var banned = {};
for (var i = 0; i < 100; i++) {
banned[''+(i * 3)] = true; // ban 0, 3, 6, 9, 12, ...
}
for (var i = o.data.length - 1; i >= 0; i--) {
if (banned[o.data[i].id]) {
o.data.splice(i, 1);
}
}
console.log(o);
// { data:
// [ { name: 'Bob', id: '1' },
// { name: 'Jake', id: '2' },
// { name: 'Jake', id: '4' },
// { name: 'Bob', id: '5' },
// { name: 'Bob', id: '7' },
// { name: 'Jake', id: '8' },
// { name: 'Jake', id: '10' },
// ...
I am assuming that you have already parsed the JSON data and you have a variable pointing to the array you want to filter. Also, you have an array with the "banned" IDs.
var data = [{
"name": "Jake",
"id": "123"
}, {
"name": "Bob",
"id": "234"
}, {
"name": "Joe",
"id": "345"
}];
var banned = ["123", "345"];
The following function wil probably do the best job that can be done in terms of performance:
// Modifies the data array "in place", removing all elements
// whose IDs are found in the "banned" array
function removeBanned(data, banned) {
// Index the "banned" IDs by writing them as the properties
// of a JS object for really quick read access later on
var bannedObj = {};
banned.forEach(function(b) { bannedObj[b] = true; });
var index = data.length - 1;
while (index >= 0) {
if (bannedObj[data[index].id]) {
data.splice(index, 1);
}
--index;
}
}
This one seems fast enough, but I'd suggest you make a free clean copy instead of modifying the existing array, - it may be faster.
function filterout(o,p,f) {
var i = 0; f = f.join();
while( o[i] ) {
if( f.match( o[i][p] ) ){ o.splice(i,1) }
i++
};
}
var filter = ["123","423"];
var object =
{
"data": [
{
"name": "John",
"id": "723"
},
{
"name": "Jake",
"id": "123"
},
{
"name": "Bob",
"id": "234"
}]
};
filterout( object.data, "id", filter );
console.log(JSON.stringify( object ));
I have this Javascript json array object;
var dataJson=[ [{v:1},{v:90}],[{"v":2},{"v":"33.7000"}] ];
I want to append this array object dataJson to another object such that it looks like this;
var chartObject = {"cols": [
{id: "t", label: "t_label", type: "number"},
{id: "s", label: "s_label", type: "number"}
], "rows": [
{c:
[{v:1},{v:90}] //dataJson[0]
},
{c:
[{"v":2},{"v":"33.7000"}] ////dataJson[1]
}
]};
How do I use a for loop to insert dataJson elements into chartObject? I am sorry I am quite new to javascript and can't even produce some starting code. Thank you very much for any help.
Try this:
...
], "rows": dataJson.map(function(row) {return {c:row};})
};
Javascript objects are pretty amazing things. Just define a new field in chartObject as an array, and then push whatever json data you want into it. It looks you want rows to be an array of objects which have an identifier for each json object, but unless you explicitly want to name each dataJson with a string, then just use an indexed array:
chartObject["rows"] = [];
for(var i = 0; i < dataJson.length; i++) {
chartObject["rows"].push(dataJson[0]);
}
Now you can access each piece of data with:
chartObject["rows"][index]
And each field in the data with:
chartObject["rows"][index]["v"]
Using the simple and clean way:
var chartObject = {"cols": [
{id: "t", label: "t_label", type: "number"},
{id: "s", label: "s_label", type: "number"}
]};
var dataJson=[ [{v:1},{v:90}],[{"v":2},{"v":"33.7000"}] ];
chartObject["rows"] = []; // start with empty array
// iterate over first dataJson array
for(var i = 0, len = dataJson[0].length; i < len; i++){
// push in array `c` mapped to the i-th element of dataJson[0]
chartObject["rows"].push({c : dataJson[0][i]["v"]});
}
console.log(chartObject);
DEMO
Ignore those [object Object] in DEMO
Sample Output:
{
cols: [{
id: "t",
label: "t_label",
type: "number"
}, {
id: "s",
label: "s_label",
type: "number"
}],
rows: [{
c: 1
}, {
c: 90
}]
}
I would like to take a JavaScript object that is formatted like:
results = {
names: [
"id",
"first_name"
],
values: [
[
1234,
"Fred"
],
[
4321,
"Joe"
],
[
1123,
"Mary"
]
]
}
and turn into this:
results = {
[id: 1234, name: "Fred"],
[id: 4321, name: "Joe"],
[id: 1123, name: "Mary"]
}
I tried doing something like this, but I can't get the structure correct:
var data = []
for (i=0; i < results['values'].length; i++ ){
var innerData = []
for (b=0; b < results['names'].length; b++ ){
innerData.push([name:results['names'][b], value: results['values'][i][b]])
}
data.push(innerData)
}
console.log(data)
Problem 1:
results = {
[id: 1234, name: "Fred"],
[id: 4321, name: "Joe"],
[id: 1123, name: "Mary"]
}
and
var data = []
and
[name:results['names'][b]…
An array [] consists a set of values in order.
An object {} consists of a set of key:value pairs.
You are using the wrong one each time. Use {} where you have [] and vice versa
Problem 2:
You say you want objects with id and name keys, but you are trying to create name and value keys. Use the property names you actually want.
Try this:
var data = [];
for (i in results['values']){
var innerData = {}
var value = results['values'][i];
for (b in value){
var key = results['names'][b];
innerData[key] = value[b];
}
data.push(innerData);
}
console.log(data);