Value of objects inside objects that give undefined result - javascript

I'm new with JavaScript and I need to retrieve information stored in this way in a globally defined object.
var customMap ={
totalValues:{
total:10000
}
carPrices:{
'CAR1':{date1:price1}
'CAR2':{date2:price2}
}
}
It has been fed using a function and when I launch console.log(customMap) I can see the whole structure of it perfectly. The problem is when I try to retrieve specific information.
I always get undefined.
I have tried with:
for (var i in customMap.totalValues){
console.log(i);
console.log(customMap.totalValues[i]);
}//It doesn't write anything in the log.
console.log(customMap.totalValues["total"]);//undefined
console.log(customMap.totalValues.total);//undefined
what I have achieve is when I query it in this way:
console.log(customMap.totalValues);
//{}
//total: 10000
console.log(Object.values(customMap.totalValues));
console.log(Object.keys(customMap.totalValues));
console.log(Object.entries(customMap.totalValues));
All give same returning message:
//[]
//length: 0
Same happens with carPrices objects. I cannot retrieve information for each car. I mean CAR1, CAR2...
I've run out of ideas. I don't know if is because of the way of accessing to the object is not correct, or the object is not correctly defined or just because is globally declared.
I'd appreciate all ideas that you could have.
# Kirill Matrosov I add below the code to give an idea of my intention. As you may notice the object structure is bigger than the previous one because I try to be more precise in the issue. Anyway I have discovered that JS is not sequential and callbacks don't help me at all :S
var customMap =
{
totalValues:{},
carPrices:{}
}
function addValuesToCustomMap(date,car,value){
if (!customMap.carPrices[car]){
customMap.carPrices[car] = {
dates: {},
carTotalValue:0,
carPercent:0
};
}
if (!customMap.carPrices[car].dates[date]){
customMap.carPrices[car].dates[date] = value;
}
else if (customMap.carPrices[car].dates[date]){
var auxValue = customMap.carPrices[car].dates[date];
customMap.carPrices[car].dates[date] = auxValue + value;
}
var totalValue_byCar = customMap.carPrices[car].catTotalValue;
customMap.carPrices[car].catTotalValue = totalValue_byCar + value;
if(!customMap.totalValues["total"]){
customMap.totalValues["total"]=value;
}
else{
var tot = customMap.totalValues["total"];
customMap.totalValues["total"]=tot+value;
}
}
function calculatePercentagesByCar(){
var tot = customMap.totalValues["total"];
for (var k in Object.keys(customMap.carPrices)){
var totalCarPrice = customMap.carPrices[k].carTotalValue;
var percent = totalCarPrice*100/tot;
customMap.carPrices[k].carPercent = percent;
}
}
/*
customMap={
totalValue:{
total: xxxxxx
}
carPrices:{
'CAR 1': {
dates:{
{date1:value1},
(...)
{dateN:valueN}
}
carTotalValue: yyyyyy,
carPercent: zzzz
}
(...)
'CAR N': {(...)}
}
}
*/

var customMap ={
totalValues:{
total:10000
},
carPrices:{
'CAR1':{date1:'price1'},
'CAR2':{date2:'price2'}
}
};
console.log(customMap.totalValues.total);
console.log(customMap.carPrices.CAR1);
console.log(customMap.carPrices.CAR2);

Your object is broken.
var customMap ={
totalValues:{
total:10000
}, // here you are missing a comma.
carPrices:{
'CAR1':{date1:price1} // Is this value filled by the variable price1? If not is broken and should be 'price1'.
'CAR2':{date2:price2} // Is this value filled by the variable price2? If not is broken and should be 'price2'.
}
}

The problem might be that you forgot to separate the values in the customMap object and the carPrices property with commas.
Here's a working example of what you've tried
var customMap = {
totalValues:{
total:10000
},
carPrices:{
'CAR1':{'date1':'price1'},
'CAR2':{'date2':'price2'}
}
}
for (var i in customMap.totalValues){
console.log('property:', i);
console.log('value:', customMap.totalValues[i]);
}
/*
property: total
value: 10000
*/
console.log(customMap.totalValues["total"]);//10000
console.log(customMap.totalValues.total);//10000
console.log(customMap.totalValues);
console.log(Object.values(customMap.totalValues));
console.log(Object.keys(customMap.totalValues));
console.log(Object.entries(customMap.totalValues));

Related

Problems in accessing sub properties from separate JavaScript file

I was trying to access sub-properties from a javascript file and it's giving me something weird!
Suppose this is my JS file named data.js
module.exports = {
something: {
name: "Something",
num: 1,
email: "something#gmail.com"
},
somethingtwo: {
name: "Something Something",
num: 2,
email: "somethingtwo#gmail.com"
},
};
In my main js file named app.js, where I need to access it, it looks like
var persons = require('./data.js');
var getAName = function() {
for(var name in persons) {
console.log(name.email);
}
}
I really don't know what goes wrong but I have been trying this for quite a long time now. The expected output is the email Ids from the data.js file but instead, i get undefined times the number of entries (if there are 2 entries in data.js, then I get 2 undefine and so on).
How can I access the email or the num from the data.js without those undefines?
console.log(name) is returning something somethingtwo
Well, name.email is undefined because name is a string.
You can test that by writing
console.log(typeof name);
Now, to solve your problem, you need to access the property correctly:
var getAName = function() {
for (var name in persons) {
console.log(persons[name].email)
}
}
Returns:
something#gmail.com
somethingtwo#gmail.com
for(var name in persons) {
//persons is actually an object not array
//you are actually iterating through keys of an object
//var name represent a key in that object
console.log(persons[name]); //value corresponding to the key
}
I guess this code will give you the desired result.
You should be using console.log(persons[name].email)
require don't automatically calls the module
var DataArchive = require('./data.js');
var module = DataArchive.module;
var persons = module.exports;
var getAName = function() {
for(var person in persons) {
//person should be something (first iteration) and somethingtwo (second iteration)
console.log(person.email);
}

Group objects in array by property

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.

UnderscoreJs, Array iteration issue.. returning multiple result

I am using a underscore array iterator to manipulate a object. the result should be need to return as single time, But i am getting multiple time, as like my array length. how to correct this?
here is my function:
var ValidPaths = {
"green":{
"url":"greenController.js",
"lightgreen" :{
"url":"lightGreenController.js",
"floragreen":{
"url":"floragreenController.js",
"ultragreen":{
"url":"floragreenController.js",
}
}
}
}
}
var path = "green",
subPath = 'lightgreen/floragreen';
var paths = [path].concat(subPath.split('/')),
arr = [],prePath=ValidPaths;
function pathFinder (path,i){
if(prePath[path]){
arr.push({"label":path,"url":prePath[path]['url']});
prePath = prePath[path];
} else{
console.log("there is no path like " + path + "in ValidPaths");
}
return arr;
}
var breadCrumb = _.map(paths, function(root,i){
return pathFinder(root,i);
});
console.log("breadCrumb", breadCrumb); // giving me 3 arrays instead 1 how to solve this?
Here is the Live Demo

How to access elements inside nested objects in a Javascript loop using variable names?

I'm clueless. I have a JSON string like this which I need to check for a supplied "property" (postsome in the following example):
var index_file =
[{
"indexAB":[
{ "postsome": ["keyword_abc", "keyword_def"] },
{ "testsome": ["keyword_111", "keyword_222"] }
]
},{
"index_random": [
{ "postsome": ["keyword_abc"] }
]
}]
There my be any number of indices ("indexAB", "index_random") with n objects inside.
I need to "find" my property postsome but I cannot get it to work, because I'm struggling with the correct way of accessing the object.
So:
for (var i = 0, l = indices.length; i < l; i += 1) {
doc._id = "postsome",
index_name = "indexAB";
indices[i]["indexAB"]; // ok, returns object on correct iteration
indices[i][index_name]; // undefined
indices[i].indexAB[0][doc._id] // ok, returns undefined or keywords
indices[i][index_name][0][doc._id] // undefined
}
Question:
How can I access a nested object in loop using a variable name index_name?
This is not a direct answer to your question but I believe that it may actually help you more than giving you a complicated way to access values in your object.
If instead of this JSON object:
var index_file =
[{
"indexAB":[
{ "postsome": ["keyword_abc", "keyword_def"] },
{ "testsome": ["keyword_111", "keyword_222"] }
]
},{
"index_random": [
{ "postsome": ["keyword_abc"] }
]
}];
you would have this much simpler data structure:
var index_file =
{
"indexAB": {
"postsome": ["keyword_abc", "keyword_def"],
"testsome": ["keyword_111", "keyword_222"]
},
"index_random": {
"postsome": ["keyword_abc"]
}
};
then it would be much easier to access, using just:
var value = index_file.indexAB.postsome[0]; // no loops, no nothing
// value == "keyword_abc"
See: DEMO
I think that what you should change is your data model because currently it is something that is very far from the idea of JSON and it will always be very hard do access data in it.
A couple of issues
"indexAB" only exists on the first element in the array
you cannot have dots inside variable names.
I suggest you test whether indexAB is a property of the object before deferencing it further. See example below:
Fixed
var indices = index_file;
for (var i = 0, l = indices.length; i < l; i++) {
var doc_id = "postsome";
var index_name = "indexAB";
indices[i]["indexAB"]; // ok, returns object on correct iteration
indices[i][index_name]; // undefined
if ("indexAB" in indices[i]) {
indices[i].indexAB[0][doc_id] // ok, returns undefined or keywords
indices[i][index_name][0][doc_id] // undefined
}
}
index_name is undefined because the line prior to that raises an error
doc._id = "postname" // this causes an error
Just use a simple string
doc = "postname"

How can I call several variables that begin with the same letters and how can I call all variables that contain a certain value?

I need the following two codes
1) A code to select all variables that begin with "example"
2) A code to select all variables that have "true" as value for "available"
example1= {price:1000, size: 1000, available:true}
example2= {price:2000, size: 2000, available:false}
example3= {price:3000, size: 3000, available:true}
example4= {price:4000, size: 4000, available=true}
This is what I want to achieve with code one. As there are a lot of variables I need a quick way of doing it:
var allexampleprices=[example1.price, example2.price, example3.price, example4.price]
With the second code I want to get an array with all the names of the variables that contain the value "false"
Any help appreciated!
All of these are the exact same thing, assuming you're not in a function:
var myVar = 7;
window.myVar = 7;
window["myVar"] = 7;
Therefore, you can access any global variable (a variable defined outside a function) by using the window[ insertString ] method. If you wanted to search through every property on the window object to find one called example, you'd do:
for( var k in window ){
if(/example/.test(k)){
var myExample = window[k];
// Do stuff
}
}
I would HIGHLY recommend against this method, though, for many reasons. To start, it's a horribly bad practice to put anything in the global scope. Variables will start colliding all over the place on big projects. Also, the window object has soooooo many properties that searching through all of them is a horrible performance drain.
Having said all of that, I've devised an example of what you should do, including the helper functions to do it:
var objects =
{
example1:
{
price: 1000,
size: 1000,
available: true
},
example2:
{
price: 2000,
size: 2000,
available: false
},
example3:
{
price: 3000,
size: 3000,
available: true
},
example4:
{
price: 4000,
size: 4000,
available: true
}
}
function filter(obj, comparator){
var list = [];
for(var k in obj){
if(obj.hasOwnProperty(k)){ // fix for IE
if(comparator(obj[k], k, obj)) list.push(obj[k]);
}
}
return list;
}
function isExample(obj, key){
if(/^example/.test( key.toLowerCase() )) return true;
}
function isAvailable(obj){
if(obj.available) return true;
}
/**
* And here's how you use it
*/
var examples = filter(objects, isExample);
var available = filter(objects, isAvailable);
var availableExample = filter(examples, isAvailable);
The filter function returns an array of all of the matching objects.
--- EDIT ---
You want it to say the names of the objects in the console. I'm assuming what you mean is that the console currently shows [object, object, object, object]. There are two ways to do this:
(1) Put the name in the object itself
example1:
{
name: "example1",
price: 1000,
size: 1000,
available: true
}
(2) Capture the name in the filter operation
var names = [];
var examples = filter(objects, function(obj, name){
if(/^example/.test( name.toLowerCase() )){
names.push(name);
return true;
}
});
console.log(names);
I do like below if all variables are in global scope
var passedElements = [];
for(var i = 1, l = 100 /* Maximum number of variable */ ; i < l; i++){
if(window['example' + i]){
var temp = window['example' + i];
if(temp.available === true){
passedElements.push(temp);
}
}/*
else{
// Dont break the loop here, if any variable is missing in between
two variables it will fail. Eg : Example1, Example3.. 2 is missing.
}*/
}
console.log(passedElements);
I hope it will help.
It's seems like follwing line is generated by some logical code:
var example1= {price:1000, size: 1000, available:true}
Why dont you simply store the vaiable names in another array that should give you the solution of Q-1.
Then you can easily travers through all of the vaiables (array) to find the vairables that have "true" as value for "available"

Categories