Join JSON attributes - javascript

I am working with javascript and I would like to join two JSON file into a single JSON object that contains all the attributes. Right now the two JSON file have separate information but I need to combine them.
Station Information JSON - Example Below:
{
"last_updated":1493307962,
"ttl":10,
"data":{
"stations":[
{
"station_id":"219",
"name":"Central Square - East Boston",
"short_name":"A32036",
"lat":42.37454454514976,
"lon":-71.03837549686432,
"region_id":10,
"rental_methods":[
"KEY",
"CREDITCARD"
],
"capacity":19,
"eightd_has_key_dispenser":false
},
{
"station_id":"220",
"name":"Test 1",
"short_name":"Test 1",
"lat":0,
"lon":0,
"rental_methods":[
"KEY",
"CREDITCARD"
],
"capacity":0,
"eightd_has_key_dispenser":false
}
]
}
}
Station Status JSON - Example Below:
{
"last_updated":1493308075,
"ttl":10,
"data":{
"stations":[
{
"station_id":"219",
"num_bikes_available":7,
"num_bikes_disabled":1,
"num_docks_available":11,
"num_docks_disabled":0,
"is_installed":1,
"is_renting":1,
"is_returning":1,
"last_reported":1493283725,
"eightd_has_available_keys":false
},
{
"station_id":"220",
"num_bikes_available":0,
"num_bikes_disabled":0,
"num_docks_available":0,
"num_docks_disabled":0,
"is_installed":0,
"is_renting":0,
"is_returning":0,
"last_reported":0,
"eightd_has_available_keys":false
}
]
}
}
Specifically, I looked at this post (How to join two json object in javascript, without using JQUERY) but the two JSON files have more complex structure, so I could not make it work.
Any suggestion will be really appreciated.

This code behaves like a join on the 2nd object (but can be extended to perform a full outer join)
It handles conflicts by appending a string _conflict to the key name
I've written this one to get you started but you'll have to customize it to support your exact structure
The combined object isn't a list anymore but has the same indexes as the array.
var obj1 = {
"conflicting_key":1493307962,
"concurrent_key":10,
"data":{
"listOfEvents":[
{
"event_id":219,
"name":"Central Square - East Boston",
"rental_methods":[
"KEY",
"CREDITCARD"
],
"capacity":19
},
{
"event_id":220,
"name":"Test 1",
"lat":0,
"lon":0,
"rental_methods":[
"KEY",
"CREDITCARD"
],
"capacity":0,
"eightd_has_key_dispenser":false
}
]
}
};
var obj2 = {
"conflicting_key":1493308075,
"concurrent_key":10,
"data":{
"listOfEvents":[
{
"event_id":219,
"num_bikes_available":7,
"num_bikes_disabled":1,
"last_reported":1493283725,
"eightd_has_available_keys":false
},
{
"event_id":220,
"num_bikes_available":0,
"num_bikes_disabled":0,
"num_docks_available":0,
"is_returning":0,
"last_reported":0,
"eightd_has_available_keys":false
}
]
}
};
function combine(obj1, obj2) {
var combinedObject = Object.assign({}, obj1);
for(var key in obj2) {
if(typeof obj2[key] !== "object") {
if(obj1[key] !== obj2[key]) {
var keyName = key;
if(key in obj1) {
keyName = keyName + "_conflict";
}
combinedObject[keyName] = obj2[key];
}
} else {
combinedObject[key] = combine(obj1[key], obj2[key]);
}
}
return combinedObject;
}
console.log(combine(obj1, obj2));

I guess you want to merge stations. If both stations arrays are in the same order (as your example shows) you can do it easily this way:
First parse both JSON using JSON.parse and then merge each station object using Object.assign
var obj1 = JSON.parse('your-first-json');
var obj2 = JSON.parse('your-second-json');
obj1.data.stations.forEach(function(item, i) {
Object.assign(item, obj2.data.stations[i])
});
//obj1 will have the obj2 sation data.
If the arrays are not in the same order (same index - same ID) you'll have to perform a lookup by ID before performing the merge.
You could use Array.find for that:
obj1.data.stations.forEach(function(station, i){
var station2 = obj2.data.stations.find(function(item) {
return item.station_id === station.station_id;
});
Object.assign(station, station2);
});
I don't know where you're running this, if in node or in the browser, but there are polyfills for both Object.assign & Array.find in the links I've provided. Also there are many similar functions using jQuery or other similar libraries.
var obj1 = {
"last_updated":1493307962,
"ttl":10,
"data":{
"stations":[
{
"station_id":"219",
"name":"Central Square - East Boston",
"short_name":"A32036",
"lat":42.37454454514976,
"lon":-71.03837549686432,
"region_id":10,
"rental_methods":[
"KEY",
"CREDITCARD"
],
"capacity":19,
"eightd_has_key_dispenser":false
},
{
"station_id":"220",
"name":"Test 1",
"short_name":"Test 1",
"lat":0,
"lon":0,
"rental_methods":[
"KEY",
"CREDITCARD"
],
"capacity":0,
"eightd_has_key_dispenser":false
}
]
}
};
var obj2 = {
"last_updated":1493308075,
"ttl":10,
"data":{
"stations":[
{
"station_id":"219",
"num_bikes_available":7,
"num_bikes_disabled":1,
"num_docks_available":11,
"num_docks_disabled":0,
"is_installed":1,
"is_renting":1,
"is_returning":1,
"last_reported":1493283725,
"eightd_has_available_keys":false
},
{
"station_id":"220",
"num_bikes_available":0,
"num_bikes_disabled":0,
"num_docks_available":0,
"num_docks_disabled":0,
"is_installed":0,
"is_renting":0,
"is_returning":0,
"last_reported":0,
"eightd_has_available_keys":false
}
]
}
};
obj1.data.stations.forEach(function(item, i) {
Object.assign(item, obj2.data.stations[i])
});
console.log(obj1)

I assume you're not interested in the parameters surrounding the stations. You could use this code to get an array of stations with all the information based on station_id
obj1.data.stations.map(el1 => Object.assign({},el1,obj2.data.stations.filter(el2 => el2.station_id === el1.station_id)));
Where obj1 and obj2 are your JSONs.
"[
{
"0": {
"station_id": "219",
"num_bikes_available": 7,
"num_bikes_disabled": 1,
"num_docks_available": 11,
"num_docks_disabled": 0,
"is_installed": 1,
"is_renting": 1,
"is_returning": 1,
"last_reported": 1493283725,
"eightd_has_available_keys": false
},
"station_id": "219",
"name": "Central Square - East Boston",
"short_name": "A32036",
"lat": 42.37454454514976,
"lon": -71.03837549686432,
"region_id": 10,
"rental_methods": [
"KEY",
"CREDITCARD"
],
"capacity": 19,
"eightd_has_key_dispenser": false
},
{
"0": {
"station_id": "220",
"num_bikes_available": 0,
"num_bikes_disabled": 0,
"num_docks_available": 0,
"num_docks_disabled": 0,
"is_installed": 0,
"is_renting": 0,
"is_returning": 0,
"last_reported": 0,
"eightd_has_available_keys": false
},
"station_id": "220",
"name": "Test 1",
"short_name": "Test 1",
"lat": 0,
"lon": 0,
"rental_methods": [
"KEY",
"CREDITCARD"
],
"capacity": 0,
"eightd_has_key_dispenser": false
}
]"

Related

How to get this output from JSON in javascipt ny comparing student_id from mark_details and students_detail?

JSON Object:
{
"students_detail": [
{
"student_id": 1,
"name": "abc",
"roll_number": 10
},
{
"student_id": 2,
"name": "pqr",
"roll_number": 12
}
],
"subject_details": [
{
"subject_id": 1,
"subject_name": "math"
},
{
"subject_id": 2,
"subject_name": "english"
}
],
"exam_details": [
{
"exam_id": 1,
"exam_name": "Prelim"
}
],
"mark_details": [
{
"id": 1,
"exam_id": 1,
"subject_id": 1,
"student_id": 1,
"mark": 51
},
{
"id": 2,
"exam_id": 1,
"subject_id": 2,
"student_id": 2,
"mark": 61
}
]
}
Ouptut:
{
"student_mark_details": [
{
"abc": {
"roll_number": 10,
"Prelim": [
{
"subject_name": "math",
"mark": 51
}
]
},
"pqr": {
"roll_number": 12,
"Prelim": [
{
"subject_name": "english",
"mark": 61
}
]
}
}
]
}
i tried using loops and accesing student_id in both object and comparing them but code gets too messy and complex,is there any way i can use map() or filter() in this or any other method.
i have no idea where to start,my brain is fried i know im asking lot but help will be appreciated (any link/source where i can learn this is fine too)
Your output object really has a weird format: student_mark_details is an array of size 1 that contains an object that has all your students in it. Anyway, this should give you what you need. It is a format that you find often because it is a system with primary key and secondary key used a lot in databases.
The key to manage that is to start with what is at the core of what you are looking for (here, you want to describe students, so you should start from there), and then navigate the informations you need by using the primary/secondary keys. In JS, you can use the find() function in the case where one secondary key can be linked only to one primary key (ex: one mark is linked to one exam), and the filter() function when a secondary key can be linked to multiple secondary keys (ex: a student is linked to many grades).
I am not sure if this is 100% what you need because there are maybe some rules that are not shown in your example, but it solves the problem you submitted here. You might have to test it and change it depending of those rules. I don't know what your level is so I commented a lot
const data = {
"students_detail": [
{
"student_id": 1,
"name": "abc",
"roll_number": 10
},
{
"student_id": 2,
"name": "pqr",
"roll_number": 12
}
],
"subject_details": [
{
"subject_id": 1,
"subject_name": "math"
},
{
"subject_id": 2,
"subject_name": "english"
}
],
"exam_details": [
{
"exam_id": 1,
"exam_name": "Prelim"
}
],
"mark_details": [
{
"id": 1,
"exam_id": 1,
"subject_id": 1,
"student_id": 1,
"mark": 51
},
{
"id": 2,
"exam_id": 1,
"subject_id": 2,
"student_id": 2,
"mark": 61
}
]
}
function format(data) {
const output = {
"student_mark_details": [{}]
};
//I start by looping over the students_detail because in the output we want a sumary by student
data.students_detail.forEach(student => {
//Initialization of an object for a particular student
const individualStudentOutput = {}
const studentId = student.student_id;
const studentName = student.name;
//The rollNumber is easy to get
individualStudentOutput.roll_number = student.roll_number;
//We then want to find the exams that are linked to our student. We do not have that link directly, but we know that our student is linked to some marks
//Finds all the marks that correspond to the student
const studentMarkDetails = data.mark_details.filter(mark => mark.id === studentId);
studentMarkDetails.forEach(individualMark => {
//Finds the exam that corresponds to our mark
const examDetail = data.exam_details.find(exam => individualMark.exam_id === exam.exam_id);
//Finds the subject that corresponds to our mark
const subjectDetail = data.subject_details.find(subject => individualMark.subject_id === subject.subject_id);
//We then create a grade that we will add to our exam
const grade = {
subject_name: subjectDetail.subject_name,
mark: individualMark.mark
}
//We then want to add our grade to our exam, but we don't know if our output has already have an array to represent our exam
//So in the case where it does not exist, we create one
if (!individualStudentOutput[examDetail.exam_name]) {
individualStudentOutput[examDetail.exam_name] = [];
}
//We then add our grade to the exam
individualStudentOutput[examDetail.exam_name].push(grade);
});
//Now that we have finished our individual output for a student, we add it to our object
output.student_mark_details[0][studentName] = individualStudentOutput;
})
return output;
}
console.log(JSON.stringify(format(data)))

Filter out objects in array if one value is the same

I am fetching data from an api that, sometimes, gives me multiple objects with the same values, or very similar values, which I want to remove.
For example, I might get back:
[
{
"Name": "blah",
"Date": "1992-02-18T00:00:00.000Z",
"Language": "English",
},
{
"Name": "blahzay",
"Date": "1998-02-18T00:00:00.000Z",
"Language": "French",
}, {
"Name": "blah", // same name, no problem
"Date": "1999-02-18T00:00:00.000Z", // different date
"Language": "English", // but same language
},
]
So I want to check that no two objects have a key with the same "Language" value (in this case, "English").
I would like to get the general process of filtering out the entire object if it's "Language" value is duplicated, with the extra issue of not having the same number of objects returned each time. So, allowing for dynamic number of objects in the array.
There is an example here:
Unexpeected result when filtering one object array against two other object arrays
but it's assuming that you have a set number of objects in the array and you are only comparing the contents of those same objects each time.
I would be looking for a way to compare
arrayName[eachObject].Language === "English"
and keep one of the objects but any others (an unknown number of objects) should be filtered out, most probably using .filter() method along with .map().
The below snippets stores the languages that have been encountered in an array. If the current objects language is in the array then it is filtered out. It makes the assumption that the first object encountered with the language is stored.
const objs = [
{
"Name": "blah",
"Date": "1992-02-18T00:00:00.000Z",
"Language": "English",
},
{
"Name": "blahzay",
"Date": "1998-02-18T00:00:00.000Z",
"Language": "French",
}, {
"Name": "blah", // same name, no problem
"Date": "1999-02-18T00:00:00.000Z", // different date
"Language": "English", // but same language
},
],
presentLanguages = [];
let languageIsNotPresent;
const objsFilteredByLanguage = objs.filter(function (o) {
languageIsNotPresent = presentLanguages.indexOf(o.Language) == -1;
presentLanguages.push(o.Language);
return languageIsNotPresent;
});
console.log(objsFilteredByLanguage);
You could take a hash table and filter the array by checking Name and Language.
var array = [{ Name: "blah", Date: "1992-02-18T00:00:00.000Z", Language: "English" }, { Name: "blahzay", Date: "1998-02-18T00:00:00.000Z", Language: "French" }, { Name: "blah", Date: "1999-02-18T00:00:00.000Z", Language: "English" }],
hash = {},
result = array.filter(({ Name, Language }) => {
var key = `${Name}|${Language}`;
if (!hash[key]) return hash[key] = true;
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Using Set makes it easy to remove duplicates for as many keys as you like. I tried to be as verbose as possible so that each step was clear.
var objects = [{ "Name": "blah", "Date": "1992-02-18T00:00:00.000Z", "Language": "English", }, { "Name": "blah", "Date": "1998-02-18T00:00:00.000Z", "Language": "French", }, { "Name": "blah", "Date": "1999-02-18T00:00:00.000Z", "Language": "English" }];
function uniqueKeyVals(objects, key) {
const objVals = objects.map(object => object[key]); // ex. ["English", "French", "English"]
return objects.slice(0, new Set(objVals).size); // ex. { "English", "French" }.size = 2
}
function removeKeyDuplicates(objects, keys) {
keys.forEach(key => objects = uniqueKeyVals(objects, key));
return objects;
}
// can also use uniqueKeyVals(key) directly for just one key
console.log("Unique 'Language': \n", removeKeyDuplicates(objects, ["Language"]));
console.log("Unique ['Language', 'Name']: \n", removeKeyDuplicates(objects, ["Language", "Name"]));
I would use the underscore module for JavaScript and the unique function in this scenario. Here is a sample array of data objects:
let data = [{
name: 'blah',
date: Date.now(),
language: "en"
},
{
name: 'noblah',
date: Date.now(),
language: 'es'
},
{
name: 'blah',
date: Date.now(),
language: 'en'
}];
Then we can use the unique function in the underscore library to only return a copy of the data that has unique values associated with the language key:
const result = _.unique(data, 'language');

Javascript: How to insert dynamic keys into map/objects?

I am a JS beginner and am doing basic tutorials. I am trying to perform a zip function to return a list of {videoID: bookmarkID}. So take for example:
var videos = [
{
"id": 70111470,
"title": "Die Hard"
},
{
"id": 654356453,
"title": "Bad Boys"
},
{
"id": 65432445,
"title": "The Chamber"
}
],
bookmarks = [
{id: 470, time: 23432},
{id: 453, time: 234324},
{id: 445, time: 987834}
];
}
This does not work (I get unexpected token '.'):
return Array.zip(videos,bookmarks, function(v, b){
return {v.id: b.id};
});
This does, but returns a list containing {'v': bookmarkID}:
return Array.zip(videos,bookmarks, function(v, b){
return {v: b.id};
});
How do I get the video ID to be the key for the value bookmarkID? Also, are these technically maps or objects? Thanks.
Try:
return Array.zip(videos,bookmarks, function(v, b){
return {[v.id]: b.id};
});
You could map one and get the elemnt of the other with the same index.
var videos = [{ "id": 70111470, "title": "Die Hard" }, { "id": 654356453, "title": "Bad Boys" }, { "id": 65432445, "title": "The Chamber" }],
bookmarks = [{ id: 470, time: 23432 }, { id: 453, time: 234324 }, { id: 445, time: 987834 }],
zipped = videos.map(function (v, i) {
var o = {};
o[v.id] = bookmarks[i].id;
return o;
});
console.log(zipped);
.as-console-wrapper { max-height: 100% !important; top: 0; }
ES6
var videos = [{ "id": 70111470, "title": "Die Hard" }, { "id": 654356453, "title": "Bad Boys" }, { "id": 65432445, "title": "The Chamber" }],
bookmarks = [{ id: 470, time: 23432 }, { id: 453, time: 234324 }, { id: 445, time: 987834 }],
zipped = videos.map((v, i) => ({ [v.id]: bookmarks[i].id }));
console.log(zipped);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Your first attempt doesn't work because the key of the object you used is not correct. When you use object literals to define an object, keys have to be numeric or string literals.
On the other hand, you could solve that problem using array-like notation to create that key in the object. In JS there are two ways of accessing the value of a key within an object: using dot notation or array-like notation. The array-like notation allows you to access keys of an object not determined until runtime (keys that are variables). For instance:
var obj = {};
for(var i = 0; i < 3; i++)
obj[i] = i + 1;
console.log(obj);
In case you're interested, there isn't much difference in terms of performance between the two.
Getting back to your problem. Zip is a function that, in words of functional programmers, takes a pair of lists and creates a list of pairs. From my point of view (and a functional point of view I think), it might be wiser to tackle it differently from what you've done.
For instance (and just as a quick idea), you could create a function that does the zip (typical zip) and apply to the result to another function that picks the values you're interested in:
var videos = [{
"id": 70111470,
"title": "Die Hard"
}, {
"id": 654356453,
"title": "Bad Boys"
}, {
"id": 65432445,
"title": "The Chamber"
}],
bookmarks = [{
id: 470,
time: 23432
}, {
id: 453,
time: 234324
}, {
id: 445,
time: 987834
}];
// Assuming same size.
function zip(list1, list2) {
return list1.length ?
[[list1[0], list2[0]]].concat(zip(list1.slice(1), list2.slice(1))) : [];
}
function makeObj(list) {
var obj = {};
obj[list[0].id] = list[1].id;
return obj;
}
console.log(zip(videos, bookmarks).map(makeObj));
I made a simple zip using recursion (there are optimal ways to do it, and also can be done taking into account that the lists can have different sizes) but I think it can help you to grasp the idea.
Hope it helps

How to delete nested json key dynamically?

This is the sample json:
{
"search": {
"facets": {
"author": [
],
"language": [
{
"value": "nep",
"count": 3
},
{
"value": "urd",
"count": 1
}
],
"source": [
{
"value": "West Bengal State Council of Vocational Education & Training",
"count": 175
}
],
"type": [
{
"value": "text",
"count": 175
}
],
}
}
There are several ways to delete key search.facets.source:
delete search.facets.source
delete jsobObj['search']['facets']['source']
var jsonKey = 'source';
JSON.parse(angular.toJson(jsonObj), function (key, value) {
if (key != jsonKey)
return value;
});
Above 1 & 2 are not dynamic, and 3 is one of the way but not a proper way. Because if source is present in another node then it will not work. Please anybody can tell me how to delete it dynamically in any kind of nested key. Because we can not generate sequence of array dynamically in above 2.
Assuming you're starting from this:
let path = 'search.facets.source';
Then the logic is simple: find the search.facets object, then delete obj['source'] on it.
Step one, divide the path into the initial path and trailing property name:
let keys = path.split('.');
let prop = keys.pop();
Find the facets object in your object:
let parent = keys.reduce((obj, key) => obj[key], jsonObj);
Delete the property:
delete parent[prop];
I have found out another solution, it is very easy.
var jsonKey = 'search.facets.source';
eval('delete jsonObj.' + jsonKey + ';');

Create collection of objects with Underscore

Basically, I have an array with objects and they would need to be grouped together. It is kinda hard to explain, but it might be easier if I just gave you guys an example.
Result data
[
{
"Category": "Préparé",
"Sandwich": "Martino",
"Ingredient": "Ansjovis",
"Price": 3.1
},
{
"Category": "Préparé",
"Sandwich": "Martino",
"Ingredient": "Tabasco",
"Price": 3.1
},
{
"Category": "Veggie",
"Sandwich": "Gezond",
"Ingredient": "Tomaat",
"Price": 2.5
},
{
"Category": "Veggie",
"Sandwich": "Gezond",
"Ingredient": "Kaas",
"Price": 2.5
}
];
This is a basic example of what my array looks like. I cannot change this structure, it is how our API provides the data.
What I actually need is this structure:
[
{
"CategoryName": "Prépare",
"Sandwiches": [
{
"SandwichName": "Martino",
"Price": 3.1,
"Ingredients": ["Ansjovis", "Tabasco"]
}
]
},
{
"CategoryName": "Veggie",
"Sandwiches": [
{
"SandwichName": "Gezond",
"Price": 2.5,
"Ingredients": ["Tomaat", "Kaas"]
}
]
}
]
I have tried some stuff with Underscore and _.groupBy, _.sortBy, _.countBy
But alas, nothing I have tried actually works. Is this even possible with Underscore (or some other library)?
Also on a sidenote, this example might have some JSON structure mistakes, because I wrote it myself. The data provided by the API has a correct format.
The example only has 2 sandwiches, but in real-time, I'll be retrieving multiple categories with each 20 sandwiches and so on. This is just a minified example, but it provides an idea of what I need.
try this in simple js
var map = {};
results.forEach( function(obj){
map[ obj.CategoryName ] = map[ obj.CategoryName ] || [];
map[ obj.CategoryName ].push( obj );
});
var output = Object.keys(map).map( function(key){
var arr = [];
map[key].forEach(function(obj){
arr.push( {
"SandwichName": obj.SandwichName,
"Price": obj.Price,
"Ingredients": obj.Ingredients
});
});
return { "CategoryName" : key , "Sandwiches" : arr };
});
The following piece of code would do the trick for you:
var data = [...]; // this is your json-data
var result = _.map(_.uniq(_.pluck(data, 'Category')), function(category) {
var sandwiches = _.uniq(_.pluck(_.where(data, { Category: category }), 'Sandwich'));
return {
CategoryName: category,
Sandwiches: _.map(sandwiches, function(sandwich) {
return {
SandwitchName: sandwich,
Price: _.findWhere(data, { Category: category, Sandwich: sandwich }).Price,
Ingredients: _.pluck(_.where(data, { Category: category, Sandwich: sandwich }), 'Ingredient')
};
})
};
});

Categories