How can i get the last item of array? - javascript

I have an array of objects. I need to render an information from the last item of the array and I try this:
<p>{leg.segments[leg.segments.length - 1].arrivalCity.caption}, </p>
but it keeps giving an error
Cannot read property 'caption' of undefined.
It seems that I can get items from arrays with length of 2 and more (or when specifically get leg.segments[0].arrivalCity.caption but get an error when try to render array with only one item. I can't understand what the problem is since it should just return leg.segments[leg.segment[0]] when array has just one item and be fine with it.
All objects have 'caption'-key, the only question is to get it from [0] or [1] index.
The array is a part of parsed JSON, here's one element I map through for an example:
"legs" :
[
{
"segments" :
[
{
"arrivalCity" :
{
"uid" : "LED",
"caption" : "ST PETERSBURG"
}
},
{
"arrivalCity" :
{
"uid" : "LON",
"caption" : "LONDON"
}
}
]
},
{
"segments" :
[
{
"arrivalCity" :
{
"uid" : "MOW",
"caption" : "MOSCOW"
}
}
]
}
]
I need to render LONDON in the first case and MOSCOW in the second. But I can get ST PETERSBURG and MOSCOW with <p>{leg.segments[0].arrivalCity.caption}</p> or an error with <p>{leg.segments[leg.segments.length - 1].arrivalCity.caption}</p>

It is unable to reproduce your problem. Please show us your array.
Assuming your array is,
var leg={segments:[{arrivalCity:{caption:"cap1"}}]};
Then if you run;
leg.segments[leg.segments.length - 1].arrivalCity.caption
You will see the expected output as 'cap1'

... quoting the OP ...
... <p>{leg.segments[0].arrivalCity.caption}</p> or ... <p>{leg.segments[leg.segments.length - 1].arrivalCity.caption}</p>
The misunderstanding might be due to not treating legs like an array. Thus, in order to always access the last arrivalCity item of each of a segments array, one has to iterate the legs array in first place.
Using the OP's sample data and implementing a basic render function which does iterate the legs array first and only with each iteration does access always the last item of the current segments array via e.g segmentList[segmentList.length - 1], as already done correctly by the OP, nothing will fail but the expected result will render ...
LONDON
MOSCOW
... example ...
function renderListOfLastArrivalCities(dataList) {
const listNode = document.querySelector('.last-arrivals');
dataList.forEach(({ segments: segmentList }) => {
//const lastSegmentItem = segmentList[segmentList.length - 1];
//const arrivalCity = lastSegmentItem && lastSegmentItem.arrivalCity;
const arrivalCity = segmentList[segmentList.length - 1]?.arrivalCity;
if (arrivalCity) {
const itemNode = document.createElement('li');
itemNode.textContent = arrivalCity.caption || '';
listNode.appendChild(itemNode);
}
})
}
const legs = [{
"segments": [{
"arrivalCity": {
"uid":"LED",
"caption":"ST PETERSBURG"
}
}, {
"arrivalCity": {
"uid":"LON",
"caption":"LONDON"
}
}]
}, {
"segments": [{
"arrivalCity": {
"uid":"MOW",
"caption":"MOSCOW"
}
}]
}];
renderListOfLastArrivalCities(legs);
<ul class="last-arrivals"></ul>

Seems like your leg.segments[leg.segments.length - 1] which i assume be as object that doesn't have arrivalCity property. If it has then you must be doing any typo or maybe your data shape is different.
Could you share your array with us to take a look over it.

Related

Strange behavior in for-loop in JS/NodeJS

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!

javascript why does word return an array and word[0] undefined javascript

I'm trying to make my website bilingual.
so I would like to iterate my JS object translation. I can iterate it with this code :
for (var word of translation) {
console.log(word);
}
but then I get this:
from the moment I use this code I undefined
for (var word of translation) {
console.log(word[0]);
}
this is my object model
translation = [
{ English : [ ["English" , "engels"], ["lblEng",""] ]},
{dutch : [["dutch" , "nederlands"], ["lblNl",""]]},
{ second : [["second","seconde"], ["sec",""]]},
{minut : [["minut", "minut"],["min",""]]},
{ hour : [["hour","uur"],["h",""]]},
{ day :[ ["day", "dag"],["d",""]]},
{ to : [["to", "naar"],["to",""]]},
{ from : [["from", "van"],["from",""]]}
]
for...of interates over the values in your array, i.e. the objects. For example the first iteration, word = { English : [ ["English" , "engels"], ["lblEng",""] ]}
So when you do word[0] its undefined as there is no 0 property in that word object
for (var word of translation) {
console.log(word[0]);
}
The variable word will have assigned an object as follow:
{ English : [ ["English" , "engels"], ["lblEng",""] ]}
So, word[0] return undefined because the objects don't have a property 0.
Probably, what you really want to do is the following:
const translation = [
{ English : [ ["English" , "engels"], ["lblEng",""] ]},
{dutch : [["dutch" , "nederlands"], ["lblNl",""]]},
{ second : [["second","seconde"], ["sec",""]]},
{minut : [["minut", "minut"],["min",""]]},
{ hour : [["hour","uur"],["h",""]]},
{ day :[ ["day", "dag"],["d",""]]},
{ to : [["to", "naar"],["to",""]]},
{ from : [["from", "van"],["from",""]]}
];
for (let word of translation) {
// get the keys and values and loop over them.
Object.entries(word).forEach(([lang, values]) => {
//values[0] returns the first index, so you can access it as follow:
let [first] = values;
console.log(lang, first);
});
}
Thanks for al your reactions / answerds...
Of realy helped me forward, if took a while to respond becose I was making an ather partition of my web aplication.

Get multiple arrays of mongodb documents and merge them for unique values

I need to get all tags, which are stored in an array for multiple documents. I can select all needed documents by:
Collection.find({ 'metadata.tags': { $exists: true } });
The data of the first document could look like
"metadata" : {
"type" : "photo",
"tags" : [
"test",
"anything",
"something",
"more"
]
}
The second one:
"metadata" : {
"type" : "photo",
"tags" : [
"different",
"content",
"something",
"more"
]
}
Some of the elements in the arrays are duplicated. I need just unique values, so the result in this example would have six elements.
I would think of something like this for removing duplicates:
var c = a.concat(b);
var tags = c.filter( function (item, pos) { return c.indexOf(item) == pos; } );
return tags;
But my first problem is how to get access of all arrays, which could be more then just two, as the find() will give me multiple documents. How do I get the arrays of all documents and merge them?
The second problem is to remove duplicates, as my example would just work for two arrays.
Update
I would do something like this:
var tags = [];
Media.find({ 'metadata.tags': { $exists: true } }).forEach(function(doc) {
tags = tags.concat(doc.metadata.tags);
});
var result = tags.filter( function (item, pos) { return tags.indexOf(item) == pos; } );
return result;
Maybe this could be optimized...
To solve your , mongoDB has already provided us a distinct command. Use it like below:
Collection.distinct('metadata.tags')
The above command will give you distinct values from the array. There wont be any duplicate elements. Hence your second problem will also be solved

Array contained within JS Object is nested two-deep

I am building an array of objects. The structure should be like this:
var array = [
{ 'keyword' : 'somekeyword',
'guidelines' : [ "guideline1", "guideline2", "guideline3"]
},
{ 'keyword' : 'anotherkeyword',
'guidelines' : [ "guideline1", "guideline2", "guideline3"]
}
]
Instead, it's like this:
var array = [
{ 'keyword' : 'somekeyword',
'guidelines' : [[ "guideline1", "guideline2", "guideline3"]]
},
{ 'keyword' : 'anotherkeyword',
'guidelines' : [[ "guideline1", "guideline2", "guideline3"]]
}
]
What I am doing wrong in building my objects to get that second level of nesting on the guidelines array? This is the function that builds the array, reading in from a table of data in a HTML file:
$(html).find('tr').each(function() {
keywordObj = {};
guidelinesCell;
$(this).find('td').each(function() {
cellClass= $(this).attr('class');
if (cellClass === 's2') {
keywordObj.keyword = $(this).text();
} else if (cellClass === 's3' || cellClass == 's4') {
guidelinesCell = [];
guidelinesCell.push( $(this).html().split('<br>') );
//Possibly it's this push that's causing the problem?
keywordObj.guidelines = guidelinesCell;
console.log(guidelinesCell)
console.log(keywordObj.guidelines)
//At this point, the array is already nested- the console logs return eg
[Array[2]]
0: Array[2]
0: "GuidelineName1.pdf"
1: "GuidelineName2.pdf"
}
});
i ++;
if (keywordObj.guidelines && keywordObj.guidelines[0].length > 1) {
keywordsToLeitlinien.push(keywordObj);
}
});
For reference, here's a sample of the mark-up it's reading from:
<tr dir="ltr">
<td class="hd">
<p style="height:39px;">.</p>
</td>
<td dir="ltr" class="s2">samplekeyword</td>
<td dir="ltr" class="s4">GuidelineName.pdf <br>GuidelineName.pdf</td>
</tr>
EDIT to add:
This isn't a problem in my processing within this file, but I am outputting this array to a JSON file eventually, and I would rather solve the problem than keep having to work around this sort of output:
{"keyword":"keywordname","guidelines":[["guidelineName1 ","guidelineName2"]]}
Because split returns an array here:
$(this).html().split('<br>')
And since you are pushing this array into another array, it will become nested.
Instead, try something like:
guidelinesCell = $(this).html().split('<br>')
This line
guidelinesCell.push( $(this).html().split('<br>') );
.split will return an array, so it's essentially the same as pushing an array into another array.
So just assign guidelinesCell to the result of split instead
guidelinesCell = $(this).html().split('<br>');
I do think push is the problem as the jQuery function returns an array. Try pushing only the first element:
guidelinesCell.push( $(this).html().split('<br>')[0] );
//Possibly it's this push that's causing the problem?
.split splits a string into an array. .push is an operation to add an element to an array. So what you have is [] and you are pushing ["guideline values"] onto it creating the nesting.
What you should use instead of .push is .concat

JQuery - Unable to access variables which are returned in json

I am very new to programming as I have only started programming last week.
Code Part -
var office = document.getElementById('start').value;
var pickup = document.getElementById('start').value;
$.get("https://maps.googleapis.com/maps/api/distancematrix/json?origins="+office+"&destinations="+pickup+"&mode=drivinging&language=en&sensor=false",function(data){
alert(data['status']);
Data returned -
http://maps.googleapis.com/maps/api/distancematrix/json?origins=edingbourgh&destinations=london&mode=drivinging&language=en&sensor=false
I can access and display "destination_addresses", "status" and "origin_addresses" using data['status'] or data.status but unable to access anything inside rows[] I have tried every method I possibly know.
I thought that I could just do data.row.distance.text but nothing happens.
any help would be much appreciated
rows is an array, as well as elements, you need to access it like this:
var text = data.rows[0].elements[0].distance.text;
// this yields "652 km"
You want to return the first element of the array, because it has only one. rows[] doesn't access anything. You need a number within the brackets. There might be multiple objects rows or elements. Fill in the array index you want:
var text = data.rows[2].elements[5].distance.text;
// this yields text from the sixth element of the third row.
Original response:
{
"destination_addresses" : [ "London, UK" ],
"origin_addresses" : [ "Edinburgh, City of Edinburgh, UK" ],
"rows" : [
{
"elements" : [
{
"distance" : {
"text" : "652 km",
"value" : 652135
},
"duration" : {
"text" : "6 hours 44 mins",
"value" : 24247
},
"status" : "OK"
}
]
}
],
"status" : "OK"
}
try
alert(data['rows'][0].elements[0].distance.text);
output 625km
Include jquery and try to parse the json data returned using JSON.parse which would give you a javascript array object
var dataArray = JSON.parse(data['status']);
alert(dataArray['status']);
Hope this helps
first parse the json into a real object:
try {
var realObject = JSON.parse(jsonString);
} catch (err) {
console.log("error parsing json string");
}
and than you should be able to access via:
var text = data.rows[0].elements[0].distance.text;

Categories