This is my JSON, I want to directly get the zipCodes values from the JSON without looping through the JSON. How can I do it?
countries:[
{
name:'India',
states:[{
name:'Orissa',
cities:[{
name:'Sambalpur',
zipCodes:{'768019','768020'}
}]
}]
}
]
I think you are looking for
countries[0].states[0].cities[0].zipCodes
Please note, this works for the above JSON as there is only 1 country in countries array and same as for states and cities. However, if there are more than 1 country, state or city then, you will have to iterate to extract information until and unless you know the exact index.
As this is not an associative array, your option is only to use indexes like this:
countries[x].states[y].cities[0].zipCodes
Where x would be each representation of state in your array, in case, of course, that you have more than one.
Similarly y would be each state in each state in each country, in case you have more of those and you can do the same for cities if you need to.
EDIT:
Here's how you can iterate the array:
for(var c in countries)
{
var name = countries[c].name;
if (name === "CountryIAmLookingFor")
{
var statesList = countries[c].states;
for (var s in statesList)
{
var stateName = statesList[s].name;
.....
}
}
}
You can keep iterating until you find the country, state, and city you need, then extract the zipCodes from there as shown in the previous code snippet.
Without "looping"
You can do this crazy trick (not saying this is the best way, but this way you aren't looping through the JSON):
var myData = { 'Put Your Data': 'HERE' };
function getCodes(name, data) {
var sv = data.match(new RegExp(name+'([\\S\\s]*?}][\\S\\s]*?}])'))[1].match(/zipCodes":\[(.*?)\]/g), r = [];
sv.forEach(function (item) {
item.match(/\d+/g).forEach(function (sub) {
r.push(+sub);
});
});
return r;
}
getCodes('India', JSON.stringify(myData));
If your data is already string, then you don't need the JSON.stringify. The forEach you see isn't actually "looping" through the JSON. It's already extracted the zip codes and the code just adds the zip codes to the array. . This line:
var sv = JSON.stringify(data).match(new RegExp(name+'([\\S\\s]*?}][\\S\\s]*?}])'))[1].match(/zipCodes":\[(.*?)\]/g), r = [];
is what grabs the zip codes, it gets something like:
["zipCodes":["768019","768020"]"]
The next line:
item.match(/\d+/g)
will grab the numbers outputting something like:
["768019", "768020"]
The loop just adds the zip-codes to another array
With looping
You're better off looping through the JSON:
var myData = {}, // Your data
zips = [];
myData.countries.forEach(function(i) {
if (i.name === 'India') {
i.states.forEach(function(j) {
j.cities.forEach(function(l) {
l.zipCodes.forEach(function(m) {
zips.push(m);
});
});
});
}
});
//use "zips" array
PERFORMANCE AND SPEED TESTS
After testing copying an array about 500MB (half a gig) took about 30 seconds. That's a lot. Considering an extremely large JSON would be about ~5MB, looping through a little over 5MB of JSON takes about 0.14 seconds. You should never worry about speed.
Here's my "trick" for avoiding explicit iteration. Let JSON.parse or JSON.stringify do the work for you. If your JSON is in string form, try this:
var array = [];
JSON.parse(jsonString, function (key, value) {
if (key === "zipCodes") {
array = array.concat(value);
}
return value;
});
console.log(array); // all your zipCodes
Suppose your Json is like
countries =[
{
name:'India',
states:[{
name:'Orissa',
cities:[{
name:'Sambalpur',
zipCodes:768019768020
}]
},{
name:'mumbai',
cities:[{
name:'rea',
zipCodes:324243
}]
}]
}
]
So now we use MAP it will give you ZipCode of every cities
countries.map(function(s){
s.states.map(function(c){
c.cities.map(function(z){
console.log(z.zipCodes)
})
})
})
OR
If you use return statement then it will give you 2 array with two zip code as per over JSON
var finalOP = countries.map(function(s){
var Stalist = s.states.map(function(c){
var zip = c.cities.map(function(z){
return z.zipCodes
})
return zip
})
return Stalist
})
console.log(finalOP)
Related
This is my array:
var country = ["US(+1)","IND(+91)"];
And i want to convert my array in this below format:
country = [
{
title: "US(+1)",
},
{
title: "IND(+91)",
}
]
word title should be same for each array value.
with this code am trying to get my expected result as above
var obj = country.reduce(function(o, val) { o['title'][] = val; return o; }, {});
But my output is comes like this as below: only last index is taking place
{"title":"IND(+91)"} this is wrong output which i dont want
You may be able to do it with reduce but it's much easier to use map:
var country = ["US(+1)","IND(+91)"];
var obj = country.map(function(c){return {title:c}});
console.log("country:", country);
console.log("obj:", obj);
map is for when you want to turn an array of things into another array of things, and reduce is when you want to turn an array of things into just a single thing.
var country = ["US(+1)","IND(+91)"];
I would use a more descriptive word since it is a list of countries.
var countries = ["US(+1)","IND(+91)"];
But to answer your question, to manipulate an array into a new array, I like to use the array.map method:
var objects = countries.map(function(country){ return { title: country } });
Here is the documentation for map:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map?v=control
My data is as follows.
data=[ {"venue":"Foyer","values":[{"avg":996.8,"sessions":[{"minTime":0,"maxTime":599,"count":50},
{"minTime":600,"maxTime":1199,"count":39},
{"minTime":1800,"maxTime":2399,"count":43},
{"minTime":2400,"maxTime":2999,"count":48}],"timestamp":1449360000},
{"avg":986.4,"sessions":[{"minTime":0,"maxTime":599,"count":152},
{"minTime":600,"maxTime":1199,"count":51},
{"minTime":1200,"maxTime":1799,"count":36},
{"minTime":1800,"maxTime":2399,"count":68},
{"minTime":3000,"maxTime":3599,"count":34}],"timestamp":1452540000}]},
{"venue":"Venue1","values":[{"avg":996.8,"sessions":[{"minTime":0,"maxTime":599,"count":62},
{"minTime":600,"maxTime":1199,"count":33},
{"minTime":1800,"maxTime":2399,"count":37},
{"minTime":2400,"maxTime":2999,"count":24}],"timestamp":1449360000},
{"avg":986.4,"sessions":[{"minTime":0,"maxTime":599,"count":34},
{"minTime":600,"maxTime":1199,"count":52},
{"minTime":1200,"maxTime":1799,"count":21},
{"minTime":1800,"maxTime":2399,"count":142},
{"minTime":3000,"maxTime":3599,"count":23}],"timestamp":1452540000}]},
{"venue":"Venue2","values":[{"avg":996.8,"sessions":[{"minTime":0,"maxTime":599,"count":44},
{"minTime":600,"maxTime":1199,"count":25},
{"minTime":1800,"maxTime":2399,"count":34},
{"minTime":2400,"maxTime":2999,"count":23}],"timestamp":1449360000},
{"avg":986.4,"sessions":[{"minTime":0,"maxTime":599,"count":31},
{"minTime":600,"maxTime":1199,"count":11},
{"minTime":1200,"maxTime":1799,"count":122},
{"minTime":1800,"maxTime":2399,"count":12},
{"minTime":3000,"maxTime":3599,"count": 3}],"timestamp":1452540000}]}]
I want to filter the records from "values" with a matching timestamp only. The entries in "values" for non matching timestamps should be filtered, but the output should be of the same structure of data,
Expected Output:
[{"venue":"Foyer","values":[{"avg":996.8,"sessions":[{"minTime":0,"maxTime":599,"count":50},
{"minTime":600,"maxTime":1199,"count":39},
{"minTime":1800,"maxTime":2399,"count":43},
{"minTime":2400,"maxTime":2999,"count":48}],"timestamp":1449360000}]},
{"venue":"Venue1","values":[{"avg":996.8,"sessions":[{"minTime":0,"maxTime":599,"count":62},
{"minTime":600,"maxTime":1199,"count":33},
{"minTime":1800,"maxTime":2399,"count":37},
{"minTime":2400,"maxTime":2999,"count":24}],"timestamp":1449360000}]},
{"venue":"Venue2","values":[{"avg":996.8,"sessions":[{"minTime":0,"maxTime":599,"count":44},
{"minTime":600,"maxTime":1199,"count":25},
{"minTime":1800,"maxTime":2399,"count":34},
{"minTime":2400,"maxTime":2999,"count":23}],"timestamp":1449360000}]}]
I have tried with the following code, but only last venue(venue2) is appeared.
Code:
d3.json("Dwell.json", function(error,data){
data.forEach(function(a) {
resultfilter={ "venue": a.venue, "values":a.values.filter(function (d) { return d.timestamp = 1449360000 } ) }
})
document.write('<pre>' + JSON.stringify(resultfilter) + '</pre>');
It's because you're overwriting resultfilter at each run of the forEach call.
You probably want to instead do something like resultfilter.push( <FILTER RESULT>) in each forEach loop after first initializing the resultfilter array. Something like this:
resultfilter = [];
data.forEach(function(a) {
resultfilter.push(...);
})
(Also, looks like the data object you copied has some issues. If it was not related to copy/pasting into this question, then give that a check too)
Can streamline this by using Array.prototype.map()
var resultfilter = data.map(function(item) {
item.values = item.values.filter(function(d) {
return d.timestamp = 1449360000
});
return item;
});
I'm trying to simply replace the invalid date with an empty string. I'm iterating through an array of objects, but whenever I try to use _.each() I get lost. If someone could show me a way to iterate through all the fieldsToCheck items in my list, that would be rad.
massage.removeBadDates = function(data){
var fieldsToCheck = [
"partsLeadTime",
"statusDate",
"targetDate",
"revisedTargetDate",
"quoteDate",
"dispositionDate",
"serviceDate",
"finalDate",
"receivedDate"]
var newData = []
_.map(data, function(value, index, list){
newData.push(value)
//single
if (list[index].partsLeadTime == "1900-01-01T00:00:00"){
newData[index].partsLeadTime = ""
}
});
return newData
};
You pretty much want something like this:
_.each(fieldsToCheck(function(field) {
if (list[index][field] == "1900-01-01T00:00:00") {
newData[index][field] = ""
}
});
I have just started to pick up coding seriously. :)
I came across a problem that seems too complicated for me.
How to group the following products by promotions type?
var data = [
{
name:'product1',
price:'40',
promotion:[
{
name:'Buy 3 get 30% off',
code:'ewq123'
},
{
name:'Free Gift',
code:'abc140'
}
]
},
{
name:'product2',
price:'40',
promotion:[
{
name:'Buy 3 get 30% off',
code:'ewq123'
}
]
},
{
name:'product3',
price:'40',
promotion:[
{
name:'Buy 3 get 30% off',
code:'ewq123'
}
]
},
{
name:'product4',
price:'40'
},
{
name:'product5',
price:'40',
promotion:[
{name:'30% off', code:'fnj245'}
]
},
{
name:'product6',
price:'0',
promotion:[
{
name:'Free Gift',
code:'abc140'
}
]
}
];
I would like to get result in the following format
result =[
{
name : 'Buy 3 get 30% off',
code: 'ewq123',
products: [
... array of products
]
},
{
name : '30% off',
code: 'fnj245',
products: [
... array of products
]
},
{
...
}
];
I am able to get a list of products by promotion code, but how can I make it generic?
function productHasPromo(product, promotion){
if(!product.hasOwnProperty('promotion')) return false;
var productPromo = product.promotion;
for(var i=0; i<productPromo.length; i++){
if(productPromo[i].code === promotion){
return true;
}
}
return false;
}
function groupProductByPromo(products, promotion){
var arr = [];
for(var i=0; i<products.length; i++){
if(productHasPromo(products[i], promotion)){
arr.push(products[i]);
}
}
return arr;
}
Explanation
You could write a function that loops through your array and search for the unique values within a specified property. That is easily done when working with simple data types, but can be done with more complex structures as arrays of objects (like in your example), using a helper grouping function.
Since you also need the output to be in a specific format after the grouping, we will have to work on a transformer also. This transformer will receive the original data and the unique values extracted by the grouping function, and will generate the desired output.
The following functions were used in the example:
Array.prototype.groupBy = function (property, grouping, transformer) {
var values = [];
this.forEach(function (item) {
grouping.call(this, item, property).forEach(function (item) {
if (!values.contains(property, item[property])) {
values.push(item);
}
});
});
return transformer.call(this, values);
};
Array.prototype.contains = function (key, value) {
return this.find(function (elm) {
return elm[key] === value;
});
};
function transformerFunction(values) {
this.forEach(function (item) {
if (!item.promotion) return;
item.promotion.forEach(function (promotion) {
values.forEach(function (option) {
if (option.code === promotion.code) {
if (option.products) {
option.products.push(item);
} else {
option.products = [item];
}
}
});
});
});
return values;
}
function groupingFunction(item, property) {
if (!item.promotion) return [];
var values = [];
item.promotion.forEach(function (promotion) {
if (!values.contains(property, promotion[property])) {
values.push(promotion);
}
});
return values;
}
Usage as follows:
var items = data.groupBy('code', groupFunction, transformFunction);
Example
Check the example i've prepared at jsfiddle
Welcome to the coding world. A lot of people start off with a problem by trying to write some code, then they wonder why it doesn't work and scratch their heads, don't know the basics of debugging it, and then post here to SO. They're missing the crucial first step in programming which is to figure out how you are going to do it. This is also called designing the algorithm. Algorithms are often described using something called pseudo-code. It has the advantage that it can be looked at and understood and established to do the right thing, without getting bogged down in all the mundane details of a programming language.
There are some algorithms that are figured out by some very smart people--like the Boyer-Moore algorithm for string matching--and then there are other algorithms that programmers devise every day as part of their job.
The problem with SO is that all too often someone posts a question which essentially about an algorithm, and then all the keyboard-happy code jockeys pounce it and come up with a code fragment, which in many cases is so contorted and obtuse that one cannot even see what the underlying algorithm is.
What is the algorithm you propose for solving your problem? You could post that, and people would probably give you reasonable comments, and/or if you also give an actual implementation that doesn't work for some reason, help you understand where you've gone wrong.
At the risk of robbing you the pleasure of devising your own algorithm for solving this problem, here's an example:
Create an empty array for the results.
Loop through the products in the input.
For each product, loop through its promotions.
Find the promotion in the array of results.
If there is no such promotion in the array of results, create a new one, with an empty list of products.
Add the product to the array of products in the promotion entry in the array.
In pseudo-code:
results = new Array // 1
for each product in products (data) // 2
for each promotion in promotions field of product // 3
if results does not contain promotion by that name // 4
add promotion to results, with empty products field // 5
add product to products field of results.promotion // 6
If we believe this is correct, we can now try writing this in JavaScript.
var result = []; // 1
for (var i = 0; i < data.length; i++) { // 2
var product = data[i];
var promotions = product.promotion;
for (var j = 0; j < promotions.length; j++) { // 3
var promotion = promotions[i];
var name = promotion.name;
var result_promotion = find_promotion_by_name(name);
if (!result_promotion) { // 4
result_promotion = { name: name, products: [], code: promotion.code };
result.push(result_promotion); // 5
}
result_promotion.products.push(name); // 6
}
}
This code is OK, and it should get the job done (untested). However, it is still a bit unreadable. It does not follow the pseudo-code very closely. It somehow still hides the algorithm. It is hard to be sure that it is completely correct. So, we want to rewrite it. Functions like Array#foreach make it easier to do this. the top level can simply be:
var result = [];
data.forEach(processProduct);
In other words, call the processProduct function for each element of data (the list of products). It will be very hard for this code to be wrong, as long as `processProduct is implemented incorrectly.
function processProduct(product) {
product.promotion.forEach(processPromotion);
}
Again, this logic is provably correct, assuming processPromotion is implemented correctly.
function processPromotion(promotion) {
var result_promotion = getPromotionInResults(promotion);
result_promotion.products.push(name);
}
This could hardly be clearer. We obtain the entry for this promotion in the results array, then add the product to its list of products.
Now we need to simply implement getPromotionInResults. This will include the logic to create the promotion element in the results array if it doesn't exist.
function getPromotionInResults(promotion) {
var promotionInResults = findPromotionInResultsByName(promotion.name);
if (!promotionInResults) {
promotionInResults = {name: promotion.name, code: promotion.code, products: []};
result.push(promotionInResults);
}
return promotionInResults;
}
This also seems demonstrably correct. But we still have to implement findPromotionInResultsByName. For that, we can use Array#find, or some equivalent library routine or polyfill:
function findPromotionInResultsByName(name) {
return result.find(function(promotion) {
return promotion.name === name;
});
}
The entire solution is thus
function transform(data) {
// Given a product, update the result accordingly.
function processProduct(product) {
product.promotion.forEach(processPromotion);
}
// Given a promotion, update its list of products in results.
function processPromotion(promotion) {
var result_promotion = getPromotionInResults(promotion);
result_promotion.products.push(name);
}
// Find or create the promotion entries in results.
function getPromotionInResults(promotion) {
var promotionInResults = findPromotionInResultsByName(promotion.name);
if (!promotionInResults) {
promotionInResults = {name: promotion.name, code: promotion.code, products: []};
result.push(promotionInResults);
}
return promotionInResults;
}
// Find an existing entry in results, by its name.
function findPromotionInResultsByName(name) {
return result.find(function(promotion) {
return promotion.name === name;
});
}
var result = [];
data.forEach(processProduct);
return result;
}
Ok, after a few hours of works, with lots of help online and offline, I finally made it works. Thanks for the people who has helped.
Please do comment if you have a more elegant solution, always love to learn.
For people who ran into similar problem:
Here is my solution
function groupProductsByPromo(data){
var result = [];
// filter only product with promotion
var productsWithPromo = data.filter(function(product){
return product.hasOwnProperty('promotions');
});
// create promotions map
var mappedProducts = productsWithPromo.map(function(product) {
var mapping = {};
product.promotions.forEach(function(promotion) {
mapping[promotion.code] = {
promotion: promotion
};
});
return mapping;
});
// reduce duplicates in promotion map
mappedProducts = mappedProducts.reduce(function(flattenObject, mappedProducts) {
for (var promoCode in mappedProducts) {
if (flattenObject.hasOwnProperty(promoCode)) {
continue;
}
flattenObject[promoCode] = {
code: promoCode,
name: mappedProducts[promoCode].promotion.name
};
}
return flattenObject;
}, {});
// add products to promo item
for(var promoCode in mappedProducts){
mappedProducts[promoCode].products = productsWithPromo.filter(function(product){
return product.promotions.some(function(promo){
return promo.code === promoCode;
});
});
result.push(mappedProducts[promoCode]);
}
return result;
}
Check out lodash - a nifty library for doing all sorts of transforms.
lodash.groupBy is what you're looking for.
I have a dataset of records that look like this :
[{
"d1d":"2015-05-28T00:00:00.000Z",
"d1h":0,
"d15m":0,
"ct":3
},
{
"d1d":"2015-05-28T00:00:00.000Z",
"d1h":0,
"d15m":0,
"ct":1
}
]
The ct value changes in every record. If d1d, d1h, and d15m are the same in one or more records, I need to combine those records into one with the sum of all the ct values.
I do have jquery, can I use grep for this?
I realize the server side could do a better job of getting me this data , but I have zero control over that.
You don't have to use jQuery for this, vanilla JavaScript will do.
I'll show you two solutions to your problem;
Example 1: Abusing Array#reduce as an iterator
var intermediaryArray = [];
dataset.reduce(function(prev, curr) {
if(prev.d1d === curr.d1d && prev.d1h === curr.d1h && prev.d15m === curr.d15m) {
intermediaryArray.push({
d1d: prev.d1d,
d1h: prev.d1h,
d15m: prev.d15m,
ct: prev.ct + curr.ct
});
} else {
// push the one that wasn't the same
intermediaryArray.push(curr);
}
// return current element so reduce has something to work on
// for the next iteration.
return curr;
});
Example 2: Using Array#Map and Array#Reduce in conjunction
This example utilises underscore.js to demonstrate the logic behind what you want to do.
.map() produces the new array of grouped objects.
.groupBy() produces an array of subarrays containing the objects that pass the predicate that all objects must share the same d1d or grouping function.
.reduce() boils all subarrays down to one value, your object with both cts added to each other.
var merged = _.map(_.groupBy(a, 'd1d'), function(subGroup) {
return subGroup.reduce(function(prev, curr) {
return {
d1d: prev.d1d,
d1h: prev.d1h,
d15m: prev.d15m,
ct: prev.ct + curr.ct
};
});
});
Here's one possible solution:
var dataset = [{
"d1d":"2015-05-28T00:00:00.000Z",
"d1h":0,
"d15m":0,
"ct":3
},
{
"d1d":"2015-05-28T00:00:00.000Z",
"d1h":0,
"d15m":0,
"ct":1
}
]
function addCt(dataset) {
var ctMap = {}
var d1d, d1h, d15m, ct, key, value
for (var ii=0, record; record=dataset[ii]; ii++) {
key = record.d1d+"|"+record.d1h+"|"+record.d15m
value = ctMap[key]
if (!value) {
value = 0
}
value += record.ct
ctMap[key] = value
}
return ctMap
}
ctMap = addCt(dataset)
console.log(ctMap)
// { "2015-05-28T00:00:00.000Z|0|0": 4 }
You may want to construct the key in a different way. You may want set the value to an object containing the d1d, d1h, d15m and cumulated ct values, with a single object for all matching d1d, d1h and d15m values.