I'm working on a NodeJS/VueJS app and I encounter some strange behavior and I am not sure if it is something I am doing wrong or something else.
I have 2 arrays, one array of objects that contains campaigns and one array of objects that contains all promotions associated to all campaigns.
Each Campaign object contains, at this point, an empty array called promos, where I want to push all the promotions for each campaign.
for (var i = 0; i < campaigns.length; i++) {
for (var j = 0; j < campaignsPromo.length; j++) {
if (campaignsPromo.length > 0) {
if (campaigns[i].IDCampaign == campaignsPromo[j].IDCampaign) {
if ((campaignsPromo[j].associated == 1) && (campaignsPromo[j].validated == 1)) {
campaigns[i].promos.push(campaignsPromo[j]);
console.log("Validated Campaign ID " + campaigns[i].IDCampaign);
console.log("Promo ID " + campaignsPromo[j].IDPromo);
} else if ((campaignsPromo[j].associated == 1) && (campaignsPromo[j].validated == 0)) {
campaigns[i].unvalidatedPromos++;
console.log("Unvalidated Campaign ID " + campaigns[i].IDCampaign);
console.log("Promo ID " + campaignsPromo[j].IDPromo);
}
}
} else {
console.log("No promos!");
}
}
}
At first, the code seems to be doing what is supposed to do and it checks out with my test data set.
However, in the end, all campaigns end up having the same promotions.
Campaigns With No Promos: [{"IDCampaign":7,"campaignName":"dadsadafds","startDate":"2022-02-03","endDate":"2022-02-28","unvalidatedPromos":0,"promos":[]},{"IDCampaign":3,"campaignName":"Tarzan 3","startDate":"2022-02-02","endDate":"2022-02-06","unvalidatedPromos":0,"promos":[]},{"IDCampaign":1,"campaignName":"Tarzan","startDate":"2022-02-01","endDate":"2022-03-01","unvalidatedPromos":0,"promos":[]},{"IDCampaign":2,"campaignName":"Tarzan 2","startDate":"2022-02-01","endDate":"2022-02-08","unvalidatedPromos":0,"promos":[]},{"IDCampaign":4,"campaignName":"Tarzan 4","startDate":"2022-02-01","endDate":"2022-02-05","unvalidatedPromos":0,"promos":[]},{"IDCampaign":5,"campaignName":"Jabe 1","startDate":"2022-02-01","endDate":"2022-02-05","unvalidatedPromos":0,"promos":[]},{"IDCampaign":6,"campaignName":"dadsada","startDate":"2022-02-01","endDate":"2022-02-08","unvalidatedPromos":0,"promos":[]},{"IDCampaign":8,"campaignName":"Black Friday","startDate":"2022-02-01","endDate":"2022-02-28","unvalidatedPromos":0,"promos":[]}]
Validated Campaign ID 1
Promo ID 1119
Unvalidated Campaign ID 1
Promo ID 107
Campaigns With Promos: [{"IDCampaign":7,"campaignName":"dadsadafds","startDate":"2022-02-03","endDate":"2022-02-28","unvalidatedPromos":0,"promos":[{"IDCampaign":1,"IDPromo":1119,"promoType":"CHRISTMASS","associated":1,"validated":1,"promoName":"Test Promo 1","beginDate":"2020-11-26","endingDate":"2020-11-29"}]},{"IDCampaign":3,"campaignName":"Tarzan 3","startDate":"2022-02-02","endDate":"2022-02-06","unvalidatedPromos":0,"promos":[{"IDCampaign":1,"IDPromo":1119,"promoType":"CHRISTMASS","associated":1,"validated":1,"promoName":"Test Promo 1","beginDate":"2020-11-26","endingDate":"2020-11-29"}]},{"IDCampaign":1,"campaignName":"Tarzan","startDate":"2022-02-01","endDate":"2022-03-01","unvalidatedPromos":1,"promos":[{"IDCampaign":1,"IDPromo":1119,"promoType":"CHRISTMASS","associated":1,"validated":1,"promoName":"Test Promo 1","beginDate":"2020-11-26","endingDate":"2020-11-29"}]},{"IDCampaign":2,"campaignName":"Tarzan 2","startDate":"2022-02-01","endDate":"2022-02-08","unvalidatedPromos":0,"promos":[{"IDCampaign":1,"IDPromo":1119,"promoType":"CHRISTMASS","associated":1,"validated":1,"promoName":"Test Promo 1","beginDate":"2020-11-26","endingDate":"2020-11-29"}]},{"IDCampaign":4,"campaignName":"Tarzan 4","startDate":"2022-02-01","endDate":"2022-02-05","unvalidatedPromos":0,"promos":[{"IDCampaign":1,"IDPromo":1119,"promoType":"CHRISTMASS","associated":1,"validated":1,"promoName":"Test Promo 1","beginDate":"2020-11-26","endingDate":"2020-11-29"}]},{"IDCampaign":5,"campaignName":"Jabe 1","startDate":"2022-02-01","endDate":"2022-02-05","unvalidatedPromos":0,"promos":[{"IDCampaign":1,"IDPromo":1119,"promoType":"CHRISTMASS","associated":1,"validated":1,"promoName":"Test Promo 1","beginDate":"2020-11-26","endingDate":"2020-11-29"}]},{"IDCampaign":6,"campaignName":"dadsada","startDate":"2022-02-01","endDate":"2022-02-08","unvalidatedPromos":0,"promos":[{"IDCampaign":1,"IDPromo":1119,"promoType":"CHRISTMASS","associated":1,"validated":1,"promoName":"Test Promo 1","beginDate":"2020-11-26","endingDate":"2020-11-29"}]},{"IDCampaign":8,"campaignName":"Black Friday","startDate":"2022-02-01","endDate":"2022-02-28","unvalidatedPromos":0,"promos":[{"IDCampaign":1,"IDPromo":1119,"promoType":"CHRISTMASS","associated":1,"validated":1,"promoName":"Test Promo 1","beginDate":"2020-11-26","endingDate":"2020-11-29"}]}]
This is the content of campaignsPromo:
[ { IDCampaign: 1,
IDPromo: 1119,
promoType: 'CHRISTMASS',
associated: 1,
validated: 1,
promoName: 'Test Promo 1',
beginDate: '2020-11-26',
endingDate: '2020-11-29' },
{ IDCampaign: 1,
IDPromo: 107,
promoType: 'CHRISTMASS',
associated: 1,
validated: 0,
promoName: 'Test Promo 2',
beginDate: '2019-12-21',
endingDate: '2019-12-23' } ]
Any ideas? From where I'm standing, I am not doing anything wrong but it wouldn't be the first time I'm missing the obvious.
PS: Please ignore the fact my campaigns are called "Tarzan".
You didn't share the offending code, so I'll give a generic answer.
When the symptom is that a bunch of all objects seem to have a property that is the same array of items, it is likely caused by exactly that. Each object may be sharing the same array instance.
A typical mistake is something like the following:
var items = [];
var containers = [];
for (let i = 0; i < 3; ++i) {
items.push(i);
let container = {};
container.items = items;
containers.push(container);
}
console.log(containers);
Although one might expect, or even have intended to get 3 objects like this:
[
{ items: [ 0 ] },
{ items: [ 0, 1 ] },
{ items: [ 0, 1, 2 ] }
]
The items array is actually the same instance of an array. And what you actually get is more like:
[
{ items: [ 0, 1, 2 ] },
{ items: [ 0, 1, 2 ] },
{ items: [ 0, 1, 2 ] }
]
In fact, the stack snippet visualizer actually does a better job visualizing this because it outputs:
[
{
"items": [
/**id:3**/
0,
1,
2
]
},
{
"items": /**ref:3**/
},
{
"items": /**ref:3**/
}
]
In this way, it tries to inform you that it is actually the same array by giving it a ref:3 label and just marking the values of the other properties with the same ref:3 label as a comment.
Causes I see typically stem from a misunderstanding of what it means to assign an array to a property. Doing so does not create a copy of the array. Both objects refer to the same array.
This can be strange because: it can appear to be correct in a debugger by inspecting the contents of your array as your are stepping through a loop. Even if we had added log messages like console.log(items) or even console.log(container) inside the loop, we'd probably still not have enough of a clue that something went wrong because the contents of that one array instance change with each iteration of the loop and we can dump versions of it as text that appear to be correct, but then unwittingly change the contents of the array on the next iteration.
But if we log the entire containers array after the loop, you'll find that each object has the same instance of the array. Assining an array isn't bad if it's only one object that gets the array assigned to it, but if you assign the same array to properties of multiple objects, or in a loop, you may run into this problem.
One possible habit-breaker you can try is to inspect all objects in a second loop after your primary loop rather than sneaking your logging code directly into the first loop. It's less efficient, but if you're often making these kinds of mistakes, it can help you find problems and achieve correctness.
Another habit breaker is to console.log(JSON.stringify(foo)) rather than console.log(foo) because console.log in the browser actually remembers the reference of the object and shows you its current contents, rather than its contents at the time it was logged. This is different depending on the platform, where node.js will log as text instead of by reference.
Good luck and be careful!
Here is the problematic code:
let newFriend = event.target.id;
let friends;
if (sessionStorage.getItem('friends') === null || sessionStorage.getItem('friends') === undefined || sessionStorage.getItem('friends') === '') {
console.log('DEV_NO FRIENDS!sessionStorage[\'friends\']: ' + sessionStorage.getItem('friends'));
friends = [newFriend];
} else {
let currentFriends = sessionStorage.getItem('friends').split(',');
console.log(currentFriends.length);
// let currentFriends = JSON.parse(sessionStorage.getItem('friends'));
console.log('DEV_sessionStorage friends: ' + currentFriends);
console.log('DEV_inArray condition: ' + $.inArray(newFriend, currentFriends));
if (!($.inArray(newFriend, currentFriends) !== -1)) {
console.log('DEV_if not in array!');
friends = currentFriends.push(newFriend);
console.log('DEV_friends in if: ' + friends);
}
}
let data = {friends: friends};
It is hooked on image tag. The sessionStorage fills on successful login like so:
if (response['friends'] !== undefined) {
sessionStorage.setItem('friends', response['friends']);
} else {
sessionStorage.removeItem('friends');
}
Or is updated like so, if new friend is added:
ajax(url, 'GET', 'none', 'Kinvey', function(response) {
sessionStorage.setItem('friends', response['friends']);
});
The idea is: a user can add friends to his friends list. The friend is 'PUT' into my app's back-end, inside a column called 'friends'. Then sessionStorage is updated to store the new friend. To my knowledge sessionStorage supports only strings, so I thought lets store the friends as string, separated by ",". Then I would pick that up ('currentFriends') and split that string into array. Then push the next item and send the data back to the server, then update sessionStorage. But I simply cannot do it - I've been trying for over 3 hours now. As you can see with the numerous console.log()s, for some reason I cannot process my data accordingly and I have no idea what am I doing wrong. Sorry for the long post, but I'm really stuck in here..
Bottom line: as #dfasoro kindly explained - when working with REST one should always make sure he keeps his data in JSON strings. My second problem was that array.push() returns integer (length of array) instead of new array.
I hope this will help you, I have helped you refactor your code and removed unneccesaries, I hope the inline comments help you as well.
IMAGE HOOK CODE
let newFriend = event.target.id;
let friends = [];
if (sessionStorage.getItem('friends')) {
try {
//this will throw an error if invalid array json is in friends sessionStorage
friends = JSON.parse(sessionStorage.getItem('friends'));
}
catch (e) { }
}
//is friend in the array?
//if not add and store back into sessionStorage
if (friends.indexOf(newFriend) == -1) {
friends.push(newFriend);
sessionStorage.setItem('friends', JSON.stringify(friends));
}
let data = {friends: friends};
//continue with your image hook code
LOGIN CODE
//login code
if (response['friends']) {
sessionStorage.setItem('friends', JSON.stringify(response['friends']));
} else {
sessionStorage.removeItem('friends');
}
PUT CODE
//update PUT code
ajax(url, 'GET', 'none', 'Kinvey', function(response) {
sessionStorage.setItem('friends', JSON.stringify(response['friends']));
});
You basically store the data as JSON string and retrieve as JSON object. You also don't need the null, undefined, empty test etc. You are basically trying to test for a falsy value.
I also really hope that your response object is a standard JSON object mapped to a friend array and not a comma separated list of friends e.g.
{"friends": [4, 5, 3, 2]} and not `{"friends": "4, 5, 3, 2"}"
The above works perfect as sessionStorage only uses a key value pair.
Though I also use sessionJS to get/set/delete data to/from sessionStorage
maybe this will also help you.
I am trying to discern the index # of the pattern selected in the Combo-box. I need to pass this index value in order for another function to read from a file at the correct location. Essentially, selecting the a pattern in the combobox will let me do a lookup for specifications associated with the selected pattern based on the index. To the best of my knowledge the Vaadin Combobox does not have an index associated with the combobox items, but you are able to pass a different value than the displayed label: https://vaadin.com/docs/-/part/elements/vaadin-combo-box/vaadin-combo-box-basic.html (see: Using Objects as Items). This is solution I am trying to implement, however it gets tricky because I am dynamically populating the combobox items from a JSON file.
The code to dynamically populate the items:
paver = document.querySelector('#paver');
//alert('script executed');
patterns = [];
familyind=y;
$.getJSON('menu.json').done(function(data){
//alert('getJSON request succeeded!');
family = (data.gui[x].family[y].display);
for(ind = 0; ind < data.gui[x].family[y].pattern.length; ind++){
var patternLbl = data.gui[x].family[y].pattern[ind].name;
var patternObj = '{ pattern: { label: "' + patternLbl + '", value: ' + ind + ' } }';
patterns[ind] = patternObj;
}
document.getElementById("cb1").items=patterns;
})
.fail(function(jqXHR, textStatus, errorThrown)
{
alert('getJSON request failed! ' + textStatus);
})
.always(function() { }};
HTML for the ComboBox
<div id="patternSelect">
<template is="dom-bind" id="paver">
<div class="fieldset">
class="patterns" items="[[patterns]]" item-label-path="pattern.label" item-value-path="pattern.value"></vaadin-combo-box>
</div>
</template>
</div>
The output I get when I try to execute this is that the entire constructed string gets assembled into my selection choices. Theoretically, this should not have happened because the item-value-path and item-label-path were specified when declaring the combobox.
Screenshot of Output
It says: { pattern: { label: "A-3 Piece Random", value: 0 } }
WORKING TOWARDS A SOLUTION SECTION:
___________(April 27, 7:00pm)___________
Suggested solution to use,
var patternObj = { pattern: { label: patternLbl, value: ind } };
works fine in displaying labels:
However, I am using a trigger to detect when the value in the combo-box is changed and return the new value. Here is the code for the trigger:
// select template
var paver = document.querySelector('#paver');
// define the ready function callback
paver.ready = function () {
// use the async method to make sure you can access parent/siblings
this.async(function() {
// access sibling or parent elements here
var combobox = document.querySelector('#cb1')
combobox.addEventListener('value-changed', function(event) {
// FOR REFERENCE LOG ERRORS, THIS COMMENT IS ON HTML:215
console.log(event.detail.value);
patval = event.detail.value;
console.log(patval)
// Do stuff with fetched value
});
});
};
I have made the suggested change to using a 'value-changed' trigger. It works very well with two slight issues. First, it returns each of the console log calls twice (not sure why twice). Second, when I select the first combo-box item it returns my values but does not set the label as selected. This is not an issue with the other combo-box items, but the first item needs to be selected twice to have the label set. Please watch this short video for a demonstration: https://youtu.be/yIFc9SiSOUM. This graphical glitch would confuse the user as they would think they did not select a pattern when they know they had. Looking for a solution to make sure the label is set when the first item is selected.
You are setting a currently a String to patternObj while you should be setting an Object.
Try using either var patternObj = JSON.parse('{ pattern: { label: "' + patternLbl + '", value: ' + ind + ' } }'; or even simpler:
var patternObj = { pattern: { label: patternLbl, value: ind } };
Also, I would recommend initializing the patterns = [] inside the done callback to make sure you're not leaving any old items in the patterns when the data changes.
When I change a nested value on a record that is subscribed on the subscription isn't fired.
var recInfo = {};
recInfo.members = [];
recInfo.members.push({id: 1});
recInfo.members.push({id: 2});
recInfo.members.push({id: 3});
Example:
var record = client.record.getRecord('user/johan');
console.log(record.get());
Output:
{
name: 'Johan',
members: [
{id: 1},
{id: 2}
]
}
record.subscribe((data) => {
log.debug(LOG_TAG + `Team '${model.name}' has changes. Changes:`, data);
});
Now if I update a user's value, e.g.:
record.set('members[1]', {id: 2, admin: true});
I expect that the record changes the member that have id 2 with the same id but adds the admin-value, and then fire the change-event that subscribe() should notice.
The change-part happens but it doesn't fire the change-event so the subscription is never fired.
Am I doing something wrong?
The subscription works if I i.e. add a user.
EDIT:
So I found a way to make the change fire the subscription, and that is by cloning the members, make the change and then replace the entire members with the new values. However, the documents (http://deepstream.io/docs/record.html#set( path , value ) (sorry for the broken link)) states that I can do record.set( 'personalData.firstname', 'Marge' ); which will only update the firsname in personalData.
I know that I'm trying to change on an array's values, won't this work?
Can I only use set() to change on an object's value?
This is the workaround I'm using right now:
var members = lodash.clone(record.get('members'), true);
members[0].admin = true;
record.set('members', members);
I think the problem is due to using a different type of path notation.
record.set('members[1]', {id: 2, admin: true});
should work with:
record.set('members.1', {id: 2, admin: true});
The reason why is behind the scenes the json path used by deepstream doesn't differentiate between a normal js object and an array when trying to access something that exist.
I am trying to update a object in an array that is two-way binded to a directive but its not working at all.
I am doing the following to update it:
$filter('filter')
($scope.offers, {id: offer_id})[0].status = "active";
I get no console errors but if I console.log $scope.offers after the filter code is executed, the status is not updated.
What I am trying to accomplish is to update an object in $scope.offers array with a new value. Every object in the array has a id attribute that is unique which I am trying to select it by.
Hope this information is sufficient. Thanks!
Here you are updating the status of outcome of the filter expression.
That means $filter('filter')($scope.offers, {id: offer_id}) itself returns an array. And you are updating that array, not the actual array.
I believe, $scope.offers is two way binded and you are not updating it.
I hope this makes some sense. And if you can explain what exactly you want. Then I can put some more light.
EDIT
So, i think once you get the new status value and the id on which you need to update. You can simply iterate the array using simple for loop and check for id and if it matches update the status. Something like this.
for(var i=0; i<array.length; i++) {
if (array[i].id === requiredId) {
array[i].status = newStatus;
break;
}
}
Hope, this helps.
Using native JS methods the updateArrObj function below utilizes some to search for an object that matches the specified id and then using a more generic update approach will map an update object onto the original object. The main benefit of using some is twofold: you get a boolean value to indicate if an update occurred and also it will stop searching the array after it makes an update.
function updateArrObj(arr, id, update) {
return arr.some(function findObj(obj) {
if (obj.id === id) {
Object.keys(update).forEach(function updateObj(key) {
obj[key] = update[key];
});
return true;
}
});
}
var arrObj = [
{id: 1, status: 'inactive'},
{id: 2, status: 'inactive'},
{id: 3, status: 'inactive'},
{id: 4, status: 'inactive'},
{id: 5, status: 'inactive'}
];
var updated = updateArrObj(arrObj, 3, {status: 'active'});
document.write(
'<h3>Updated: ' + updated + '</h3>' +
'<pre>' + JSON.stringify(arrObj, null, 4) + '</pre>'
);
function updateArrObj(arr, id, update) {
return arr.some(function findObj(obj) {
if (obj.id === id) {
Object.keys(update).forEach(function updateObj(key) {
obj[key] = update[key];
});
return true;
}
});
}