Counting duplicate JSON array values using hashmap with javascript - javascript

I'm having trouble wrapping my mind around javascript hashmaps and JSON arrays. I'm trying to count duplicate import countries from a JSON array. For instance the data is:
[
{
"id": 1,
"import_country": "Russia",
"model": "Express 2500",
"make": "Chevrolet",
"sold_by": "Sibylla Kneale",
"sale_price": 14702
},
{
"id": 2,
"import_country": "Philippines",
"model": "G-Class",
"make": "Mercedes-Benz",
"sold_by": "Gabie Gradwell",
"sale_price": 19142
},
{
"id": 3,
"import_country": "Russia",
"model": "M",
"make": "Infiniti",
"sold_by": "Burl Pitkeathley",
"sale_price": 18395
}
]
This is what I have for code so far:
var country = [];
var temp = [];
const model = [];
const count = 0;
const response = await fetch('some api url where data is retrieved')
.then(response => response.json())
.then(data => {
var key = {};
for(i = 0; i< data.length; i++){
if(temp.indexOf(data[i].import_country) == -1){
temp.push(data[i][import_country]);
var _data = {};
}
}
});
My end goal is to have the total country count displayed on a graph.

A good way to do this would be to use a hashmap instead of arrays like you said.
If we update your code to be this:
var hashmap = {};
const response = await fetch('some api url where data is retrieved')
.then(response => response.json())
.then(data => {
var key = {};
for(i = 0; i< data.length; i++){
let country = data[i]['import_country'];
if (hashmap[country] == void(0)) { // if it doesn't exist in the hashmap
hashmap[country] = []; // create it in the map
}
hashmap[country].push(data[i]); // add it to the country array in the temp
}
})
If the data you are receiving above is correct, the output would look something like this:
{
"Russia": [
{"id":1,"import_country":"Russia","model":"Express 2500","make":"Chevrolet","sold_by":"Sibylla Kneale","sale_price":14702},
{"id":3,"import_country":"Russia","model":"M","make":"Infiniti","sold_by":"Burl Pitkeathley","sale_price":18395}
],
"Phillipines": [
{"id":2,"import_country":"Philippines","model":"G-Class","make":"Mercedes-Benz","sold_by":"Gabie Gradwell","sale_price":19142}
]
}
Now that we have the data formatted how we would like, we can loop through it to get the total country count:
... // code from above
for (var country in map) {
console.log(country + ": " + country.length + " cars");
}
That code would output:
Russia: 2 cars
Phillipines: 1 cars

Try below code -
var jsonArray = [{"id":1,"import_country":"Russia","model":"Express 2500","make":"Chevrolet","sold_by":"Sibylla Kneale","sale_price":14702},{"id":2,"import_country":"Philippines","model":"G-Class","make":"Mercedes-Benz","sold_by":"Gabie Gradwell","sale_price":19142},{"id":3,"import_country":"Russia","model":"M","make":"Infiniti","sold_by":"Burl Pitkeathley","sale_price":18395}];
var map = new Map();
for(var i=0;i<jsonArray.length;i++){
if(!map.get(jsonArray[i].import_country)){
map.set(jsonArray[i].import_country,1);
}else{
map.set(jsonArray[i].import_country, map.get(jsonArray[i].import_country) + 1);
}
}
Pseudo Code:-
Getting a JSON array.
Creating an empty map.
for i=0 to JSON Array length.
a. if the map doesn't contain the country, then set the country with count 1.
b. else set already existing key in the map by increasing with count 1.
End.

Related

How to map the json by key for such API response

This is how an API reponse looks like.
{
"keys": ["CustomerId", "Name", "Gender"],
"value": [["1", "Ram", "Male"]]
}
Angular VM
function Customer(){
return this.Customer = {
"CustId": "",
"Name": "",
"Gender": "",
}
}
Angular AJAX Call.
$http.get(url)
.then(function(data)){
var cust = new Customer();
//for now I am doing this way.
cust.Id = data.value[0][0];
cust.Name = data.value[0][1];
cust.Gender = data.value[0][1];
}
I don't want to map the properties by index.
These APIs not into my control
Is there any way, where I can map the properties by key (in response) as below.
cust.Id = data.value[0]["CustomerId"];
cust.Name = data.value[0]["Name"];
cust.Gender = data.value[0]["Gender"];
Any help/suggestion highly appreciated.
The only way I can see of achieving something like this is by finding the index of each key and using that as the index when accessing your values like this:
$http.get(url)
.then(function(data){
var cust = new Customer();
cust.Id = data.value[0][data.keys.indexOf('CustomerId')];
cust.Name = data.value[0][data.keys.indexOf('Name')];
cust.Gender = data.value[0][data.keys.indexOf('Gender')];
});
But note, this will only work if the keys and values match up ... which I assume they will.
If you are able to use ES6, the best workaround (for this poorly made API) would be to create a parser function like that:
function parseResponse(response) {
const {keys, value: objects} = response;
return objects.map(object => {
const parsedValue = {};
keys.forEach((key, index) => {
parsedValue[key] = object[index];
});
return parsedValue;
});
}
Tests:
const response = {
"keys": ["CustomerId", "Name", "Gender"],
"value": [["1", "Ram", "Male"]]
};
parseResponse(response); // Outputs [{CustomerId: "1", Name: "Ram", Gender: "Male"}]
If you wish, I could make that with ES5. Just say the word :)
EDIT
ES5 Version:
function parseResponse(response) {
var keys = response.keys;
var objects = response.value;
var response = [];
for (let i = 0; i < objects.length; i++) {
var formattedValue = {};
for (let j = 0; j < keys.length; j++) {
var key = keys[j];
formattedValue[key] = objects[i][j];
}
response.push(formattedValue);
}
return response;
}

Build nested JSON in Javascript

I would like to replicate this example data set into nested JSON using JavaScript or Angularjs or any javascript library.
Data:
PrimaryId,FirstName,LastName,City,CarName,DogName
100,John,Smith,NewYork,Toyota,Spike
100,John,Smith,NewYork,BMW,Spike
100,John,Smith,NewYork,Toyota,Rusty
100,John,Smith,NewYork,BMW,Rusty
101,Ben,Swan,Sydney,Volkswagen,Buddy
101,Ben,Swan,Sydney,Ford,Buddy
101,Ben,Swan,Sydney,Audi,Buddy
101,Ben,Swan,Sydney,Volkswagen,Max
101,Ben,Swan,Sydney,Ford,Max
101,Ben,Swan,Sydney,Audi,Max
102,Julia,Brown,London,Mini,Lucy
Javascript:
var file = reader.result;
var singleRow = readerFile.split(/\r\n|\n/);
var header = singleRow[0].split(',');
var result =[];
for ( var i=1; i < file.length; i++ ){
var elementData = singleRow[i].split(',');
elementData = elementData.filter(function(n){ return n != "" });
var Obj = {};
for ( var j=0; j < header.length; j++ ){
Obj[header[j]] = elementData[j];
/*
- How can i build child object and append back to Obj before j loop
- How can i build multiple child for same parent
*/
}
result.push(Obj);
}
console.log(" Print the JSON Object : " + JSON.stringify(result));
Desired Output:
{
"data": [
{
"City": "NewYork",
"FirstName": "John",
"PrimaryId": 100,
"LastName": "Smith",
"CarName": [
"Toyota",
"BMW"
],
"DogName": [
"Spike",
"Rusty"
]
},
{
"City": "Sydney",
"FirstName": "Ben",
"PrimaryId": 101,
"LastName": "Swan",
"CarName": [
"Volkswagen",
"Ford",
"Audi"
],
"DogName": [
"Buddy",
"Max"
]
},
{
"City": "London",
"FirstName": "Julia",
"PrimaryId": 102,
"LastName": "Brown",
"CarName": [
"Mini"
],
"DogName": [
"Lucy"
]
}
]
}
If Firstname, Lastname and City has same values then CarName and DogName values should be child object under the same parent
I reformatted your initial code a little bit, but it doesn't change the initial logic. One key observation is that even if the FirstName, LastName and City are the same, that may not be a unique person, hence you should use the PrimaryId instead, to determine uniqueness.
Look at the post-processing section for the new code:
const data = `PrimaryId,FirstName,LastName,City,CarName,DogName
100,John,Smith,NewYork,Toyota,Spike
100,John,Smith,NewYork,BMW,Spike
100,John,Smith,NewYork,Toyota,Rusty
100,John,Smith,NewYork,BMW,Rusty
101,Ben,Swan,Sydney,Volkswagen,Buddy
101,Ben,Swan,Sydney,Ford,Buddy
101,Ben,Swan,Sydney,Audi,Buddy
101,Ben,Swan,Sydney,Volkswagen,Max
101,Ben,Swan,Sydney,Ford,Max
101,Ben,Swan,Sydney,Audi,Max
102,Julia,Brown,London,Mini,Lucy`;
var singleRow = data.split(/\r\n|\n/);
var header = singleRow[0].split(',');
var result =[];
for (var i = 1; i < singleRow.length; i++) {
var elementData = singleRow[i].split(',');
elementData = elementData.filter(function(n) { return n != '' });
var Obj = {};
for ( var j=0; j < header.length; j++ ){
Obj[header[j]] = elementData[j];
}
result.push(Obj);
}
console.log(JSON.stringify(result, null, 2));
// Post-processing code starts here
const people = {};
// Create a map of unique people first
result.forEach(function (object) {
if (!people[object.PrimaryId]) {
people[object.PrimaryId] = {
City: object.City,
FirstName: object.FirstName,
PrimaryId: object.PrimaryId,
LastName: object.LastName,
CarName: [],
DogName: [],
};
}
// As you iterate through your results, if this person already exists
// add to their array of car and dogs.
people[object.PrimaryId].CarName.push(object.CarName);
people[object.PrimaryId].DogName.push(object.DogName);
});
// Convert back into an array
const peopleList = [];
Object.keys(people).forEach(function (primaryId) {
peopleList.push(people[primaryId]);
})
console.log(peopleList);
First of all, since you already know the property names, there's no point in parsing the first row.
I would do something like this:
let results = {};
for (let i = 1; i < file.length; i++) {
let entry = getEntry(results, file[i][0]);
entry.DogName.push(file[i][DOGNAME_INDEX]);
entry.CarName.push(file[i][CARNAME_INDEX]);
entry.LastName = file[i][LASTNAME_INDEX];
...
}
// and now to convert this into an array
let array = Object.keys(results).map(key => results[key]);
// retrieves or creates an entry for a given primary key
function getEntry(results, id) {
return results[id] || (results[id] = {});
}
You could also get fancier and dynamically determine what the column indexes are, but the way I have it just keeps things simple.
Made a fiddle for you, it gives the desired output with some things in a different order than you presented.
You can save the indexes of the headers:
var Index = {};
for(var k = 0; k < header.length; k++)
{
Index[header[k]] = k;
}
And keep a list of cities:
var cities = [];
....
cities.push(data[Index["City"]]);
To use for later so that you don't keep making more objects if the city already exists:
obj = result.data[cities.indexOf(data[Index["City"]])];
The JSFiddle: https://jsfiddle.net/3u28aon3/1/

JSON to Multidimensional Javascript Object

I'm loading an external JSON file into javascript, the JSON file looks like this:
[
{
"name":"Apple",
"year":8,
"records_lost":12367232
},
{
"name":"178.com",
"year":7,
"records_lost":10000000
},
{
"name":"Accendo Insurance Co. ",
"year":7,
"records_lost":175350
}
]
Eventually, I want to access the data via a Javascript object like this (don't mind the syntax). The point is that name will be a parent with its own meta-data.
"Apple":
"year":8,
"records_lost":12367232
"178.com":
"year":7,
"records_lost":10000000
This is the code I've already written for this part, which doesn't make name parent yet and only saves the last row of the JSON file into the array (+= instead of = would fix this, but delivers ugly values, obviously).
function initJSON() {
loadJSON(function(response) {
var JSONParse = JSON.parse(response);
var i;
for (i in JSONParse) {
JSONdata.name.i = JSONParse[i].name;
JSONdata.year = JSONParse[i].year;
JSONdata.recl = JSONParse[i].records_lost;
}
});
}
initJSON();
Thanks in forward.
Try utilizing Array.prototype.map() , delete operator
var data = [{
"name": "Apple",
"year": 8,
"records_lost": 12367232
}, {
"name": "178.com",
"year": 7,
"records_lost": 10000000
}, {
"name": "Accendo Insurance Co. ",
"year": 7,
"records_lost": 175350
}];
var res = data.map(function(val, key) {
var obj = {};
obj[val.name] = val;
delete obj[val.name].name;
return obj
});
document.getElementsByTagName("pre")[0].textContent = JSON.stringify(res, null, 4);
<pre></pre>
it should be :
var i;
for (i in JSONParse) {
JSONdata.name = i.name;
JSONdata.year = i.year;
JSONdata.recl = i.records_lost;
}
unless your loop was different:
var i;
for (i = 0; i < JSONParse.length; i++) {
JSONdata.name = JSONParse[i].name;
JSONdata.year = JSONParse[i].year;
JSONdata.recl = JSONParse[i].records_lost;
}

Building Array in order of income objects

I'm getting an XML and parsing it, saving it to array, the problems is that I get objects in this order:
temp1.ID = 15
temp1.name = "Dan"
temp1.phone = "32332"
temp2.ID = 12
temp2.name = "Test"
temp2.phone = 53463
temp3.ID = 2
temp3.name = "Tom"
temp3.phone = 12443
.
.
.
.
Object - its an objects that I get inside a loop while parsing XML
What I try is to save them in the same order I started to read them : Array: [temp1,temp2,temp3]
But The result of the next function is : Array: [temp3,temp2,temp1]
the function:
this.mytempect = [];
for (var i = 0; i < xml.length; i++) {
var temp = {};
temp.ID = parseXmlByTag(xml[i], "ID");
temp.name = parseXmlByTag(xml[i], "name");
temp.phone = parseXmlByTag(xml[i], "phone");
if (this.mytempect [temp .ID] == null) {
this.mytempect [temp .ID] = [];
}
this.mytempect [temp .ID].push(obj);
}
Before I save each object I check if I need to create for him a new Key or to add to existing one, in the end I get something like this:
I need to save the order in which I'm getting them so I'll save them in the order I entered them
If I understand your question here's what I think you should be doing. You seem to be confusing objects and arrays: mytempect needs to be an object if you want to store arrays against a key set by the ID.
Following your example, objects with the same key are assigned to the same array (identified by that key in the object) in the order in which they are read.
// create an object, not an array
this.mytempect = {};
for (var i = 0; i < arr.length; i++) {
var temp = {};
temp.ID = arr[i].ID;
temp.name = arr[i].name;
temp.phone = arr[i].phone;
// Don't check for null here because `this.mytempect[temp.ID]` might not exist
if (!this.mytempect[temp.ID]) {
this.mytempect[temp.ID] = [];
}
this.mytempect[temp.ID].push(temp);
}
DEMO
The demo produces an object with one object in an array under key 15, two under 12 and one under 2:
{
"2": [
{
"ID": 2,
"name": "Tom",
"phone": 12443
}
],
"12": [
{
"ID": 12,
"name": "Test",
"phone": 53463
},
{
"ID": 12,
"name": "Test",
"phone": 53462
}
],
"15": [
{
"ID": 15,
"name": "Dan",
"phone": "32332"
}
]
}
Note: you can't order the object in any way.
Perhaps you're looking for something like this
var mytempect = [],
dict = {},
i,
tmp;
for (i = 0; i < xml.length; ++i) {
tmp = {
ID: parseXmlByTag(xml[i], "ID"),
name: parseXmlByTag(xml[i], "name"),
phone: parseXmlByTag(xml[i], "phone")
};
if (!(tmp.ID in dict)) {
mytempect.push(dict[tmp.ID] = []);
}
dict[tmp.ID].push(tmp); // use fact Objects ByRef to add item
}
dict = null; // cleanup
The Array mytempect will now have indices 0, 1, 2, etc containing Arrays of all Objects which have the same ID. With your sample data you will get
mytempect[0][0].ID === 15;
mytempect[1][0].ID === 12;
mytempect[2][0].ID === 2;

Count duplicates within an Array of Objects

I have an array of objects as follows within my server side JS:
[
{
"Company": "IBM"
},
{
"Person": "ACORD LOMA"
},
{
"Company": "IBM"
},
{
"Company": "MSFT"
},
{
"Place": "New York"
}
]
I need to iterate through this structure, detect any duplicates and then create a count of a duplicate is found along side each value.
Both of the values must match to qualify as a duplicate e.g. "Company": "IBM" is not a match for "Company": "MSFT".
I have the options of changing the inbound array of objects if needed. I would like the output to be an object, but am really struggling to get this to work.
EDIT: Here is the code I have so far where processArray is the array as listed above.
var returnObj = {};
for(var x=0; x < processArray.length; x++){
//Check if we already have the array item as a key in the return obj
returnObj[processArray[x]] = returnObj[processArray[x]] || processArray[x].toString();
// Setup the count field
returnObj[processArray[x]].count = returnObj[processArray[x]].count || 1;
// Increment the count
returnObj[processArray[x]].count = returnObj[processArray[x]].count + 1;
}
console.log('====================' + JSON.stringify(returnObj));
For example:
counter = {}
yourArray.forEach(function(obj) {
var key = JSON.stringify(obj)
counter[key] = (counter[key] || 0) + 1
})
Docs: Array.forEach, JSON.stringify.
Object.prototype.equals = function(o){
for(var key in o)
if(o.hasOwnProperty(key) && this.hasOwnProperty(key))
if(this[key] != o[key])
return false;
return true;
}
var array = [/*initial array*/],
newArray = [],
ok = true;
for(var i=0,l=array.length-1;i<l;i++)
for(var j=i;j<l+1;j++)
{
if(!array[i].equals(array[j]))
newArray.push(array[i]);
}
We are required to write a JavaScript function that takes in one such array of objects. The function creates and return a new array in which no objects are repeated (by repeated we mean objects having same value for "Country" property.)
Moreover, the function should assign a count property to each object that represents the number of times they appeared in the original array.
const arr = [
{
"Country": "BR",
"New Lv1−Lv2": "#N/A"
},
{
"Country": "BR",
"New Lv1−Lv2": "#N/A"
},
{
"Country": "",
"New Lv1−Lv2": "test"
}];
const convert = (arr) => {
const res = {};
arr.forEach((obj) => {
const key = `${obj.Country}${obj["New Lv1−Lv2"]}`;
if (!res[key]) {
res[key] = { ...obj, count: 0 };
};
res[key].count += 1;
});
return Object.values(res);
};
console.log(convert(arr));
know more
With ES6, one can use Array#reduce with an object to store the counts.
let counts = arr.reduce((acc, curr)=>{
const str = JSON.stringify(curr);
acc[str] = (acc[str] || 0) + 1;
return acc;
}, {});
Demo
To create a new array without duplicates, a Set can be used with Array#filter.
let set = new Set;
let res = arr.filter(x => {
const str = JSON.stringify(x);
return !set.has(str) && set.add(str);
});
Demo

Categories