Cannot read property 'title' of undefined when accessing nested object - javascript

I am trying to read the value in businessData[i].services[i].title in my forEach loop but keep getting the following error message: TypeError: Cannot read property 'title' of undefined.
How do I correctly read the property title in the services array?
profileData and businessData object
var profileData = [{
id: 1,
notifications: [{
type: 'payment-received',
businessId: 1,
serviceId: 2,
timestamp: 1455177629000,
text: ''
}, {
type: 'accepted-booking',
businessId: 1,
serviceId: 2,
timestamp: 1454661898000,
text: ''
}, {
type: 'received-booking',
businessId: 1,
serviceId: 1,
timestamp: 1454661897000,
text: ''
}]
}];
var businessData = [{
id: 1,
categoryId: 1,
name: 'Japan Center Garage',
services: [{
id: 1,
businessId: 1,
title: 'Brake Fluid Refresh'
}, {
id: 2,
businessId: 1,
title: 'Diagnostics'
}]
}, {
id: 2,
categoryId: 1,
name: 'Rex Garage',
services: [{
id: 3,
businessId: 1,
title: 'Coolant Refresh'
}, {
id: 4,
businessId: 1
title: 'Oil Change'
}]
}, {
id: 3,
categoryId: 1,
name: 'Mission & Bartlett Garage',
services: [{
id: 5,
businessId: 1,
title: 'MOT'
}, {
id: 6,
businessId: 1,
title: 'Summer Check'
}, {
id: 7,
businessId: 1,
title: 'Winter Check'
}]
}]
getSelectedProfile function
getSelectedProfile: function(profileId) {
var profileId = parseInt(profileId);
for (var i = 0; i < profileData.length; i++) {
var profile = profileData[i];
if (profile.id === profileId) {
profile.notifications.forEach(function(value) {
console.log(businessData[1].services)
if (value.type === "payment-received") {
value.text = businessData[value.businessId].name + " has received payment for the " + businessData[value.businessId].services[value.serviceId].title + " service.";
}
else
if (value.type === "accepted-booking") {
value.text = businessData[value.businessId].name + " has accepted your booking for the " + businessData[value.businessId].services[value.serviceId].title + " service.";
}
else
if (value.type === "received-booking") {
value.text = businessData[value.businessId].name + " has received your booking for the " + businessData[value.businessId].services[value.serviceId].title + " service.";
}
});
return profile;
}
}
}

Just put services[value.serviceId-1] and for every single key as well; because Array starts from 0 index.

Related

How to collect all the objects in the object into a single array

When I click the button, I want to include all the objects in the itemSold and itemGet objects of the customers into the products array. how can I do that?
let customers = [{
active: true,
id: 1,
product: {
itemSold: [{id:1,name : 'car'}, {id:2,name : 'home'}],
itemGet: [{id:3,name : 'phone'}, {id:4,name : 'fly'}],
},
},
{
active: true,
id: 2,
product: {
itemSold: [{id:5,name : 'lamb'}, {id:6,name : 'mouse'}],
itemGet: [{id:7,name : 'mouse pad'}, {id:8,name : 'tv'}],
},
},
];
let clickButton = document.querySelector("#clickButton");
let products = [];
clickButton.addEventListener("click", getProcuts()});
function getProducts(){}
<button id="clickButton" >Click
</button>
let customers = [{
active: true,
id: 1,
product: {
itemSold: [{ id: 1, name: 'car' }, { id: 2, name: 'home' }],
itemGet: [{ id: 3, name: 'phone' }, { id: 4, name: 'fly' }],
},
},
{
active: true,
id: 2,
product: {
itemSold: [{ id: 5, name: 'lamb' }, { id: 6, name: 'mouse' }],
itemGet: [{ id: 7, name: 'mouse pad' }, { id: 8, name: 'tv' }],
},
},
];
let clickButton = document.querySelector("#clickButton");
let products = [];
clickButton.addEventListener("click", getProducts);
function getProducts() {
for (let i = 0; i < customers.length; i++) {
products.push(...customers[i].product.itemGet, ...customers[i].product.itemSold);
}
console.log(products);
}
<button id="clickButton">Click</button>
We loop our customers array and then select product property there we push both itemSold and itemGet arrays into products.
You can map over the customers and concatenate the arrays.
const customers = [
{
active: true,
id: 1,
product: {
itemSold: [
{ id: 1, name: "car" },
{ id: 2, name: "home" },
],
itemGet: [
{ id: 3, name: "phone" },
{ id: 4, name: "fly" },
],
},
},
{
active: true,
id: 2,
product: {
itemSold: [
{ id: 5, name: "lamb" },
{ id: 6, name: "mouse" },
],
itemGet: [
{ id: 7, name: "mouse pad" },
{ id: 8, name: "tv" },
],
},
},
];
const products = customers.map((customer) => {
return customer.product.itemSold.concat(customer.product.itemGet);
});
console.log(products);
const customers = [
{
active: true,
id: 1,
product: {
itemSold: [
{ id: 1, name: "car" },
{ id: 2, name: "home" },
],
itemGet: [
{ id: 3, name: "phone" },
{ id: 4, name: "fly" },
],
},
},
{
active: true,
id: 2,
product: {
itemSold: [
{ id: 5, name: "lamb" },
{ id: 6, name: "mouse" },
],
itemGet: [
{ id: 7, name: "mouse pad" },
{ id: 8, name: "tv" },
],
},
},
];
const products = customers.map((customer) => {
return customer.product.itemSold.concat(customer.product.itemGet).flat();
});
console.log(products.flat());

How to add a child node in a nested object at a particular position using javascript

I have a data structure as given below
ds: [
{ id: "0a12", pos: 0, name: "PH1" },
{
id: "8f83",
name: "PH2",
pos: 1,
children: [
{ id: "ll54", pos: 0, name: "L1" },
{
id: "97cs",
name: "L2",
pos: 1,
children: [
{ id: "80s3", pos: 0, name: "LL1" },
{ id: "2dh3", pos: 1, name: "LL2" },
],
},
],
},
{ id: "75fd", pos: 2, name: "PH3" },
{ id: "34jg", pos: 3, name: "PH4" },
],
I am creating a org chart wherein I have option to add a node to the left or right.
On clicking the node, I can get the node obj as an argument (ie) if I clicked on 'LL1' I can get that object, along with second arg called as type which will contain either 'left' or 'right'.
so now the type == 'left' then my resultant data structure should be like
ds: [
{ id: "0a12", pos: 0, name: "PH1" },
{
id: "8f83",
name: "PH2",
pos: 1,
children: [
{ id: "4", pos: 0, name: "L1" },
{
id: "5",
name: "L2",
pos: 1,
children: [
{ id: "36fd", pos: 0, name: "" }, // new node for type === 'left'
{ id: "80s3", pos: 1, name: "LL1" },
{ id: "2dh3", pos: 2, name: "LL2" },
],
},
],
},
{ id: "75fd", pos: 2, name: "PH3" },
{ id: "34jg", pos: 3, name: "PH4" },
],
and if type === 'right'
ds: [
{ id: "0a12", pos: 0, name: "PH1" },
{
id: "8f83",
name: "PH2",
pos: 1,
children: [
{ id: "4", pos: 0, name: "L1" },
{
id: "5",
name: "L2",
pos: 1,
children: [
{ id: "80s3", pos: 0, name: "LL1" },
{ id: "36fd", pos: 1, name: "" }, // new node for type === 'right'
{ id: "2dh3", pos: 2, name: "LL2" },
],
},
],
},
{ id: "75fd", pos: 2, name: "PH3" },
{ id: "34jg", pos: 3, name: "PH4" },
],
Note: for those nodes that don't have children we shouldn't initialize an empty array to children attribute
You can use Array.slice and spread operator to insert the new node into your array.
let data = [
{ id: '0a12', pos: 0, name: 'PH1' },
{
id: "8f83",
name: "PH2",
pos: 1,
children: [
{ id: '4', pos: 0, name: 'L1' },
{
id: '5',
name: 'L2',
pos: 1,
children: [
{ id: '80s3', pos: 0, name: 'LL1' },
{ id: '2dh3', pos: 1, name: 'LL2' },
],
},
],
},
{ id: '75fd', pos: 2, name: 'PH3' },
{ id: '34jg', pos: 3, name: 'PH4' },
];
function addNode(direction, idElement, ptr) {
const elementIndex = ptr.findIndex(x => x.id === idElement);
if (elementIndex === -1) {
// Go deeper
return ptr.map(x => x.children ? {
...x,
children: addNode(direction, idElement, x.children),
}: x);
}
const offset = direction === 'left' ? 0 : 1;
// Insert the new node in the correct position
const mutatedPtr = [
...ptr.slice(0, elementIndex + offset),
{ id: 'XXXX', pos: elementIndex + offset, name: '' },
...ptr.slice(elementIndex + offset),
];
// change the positions
mutatedPtr.forEach((x, xi) => {
x.pos = xi;
});
return mutatedPtr;
}
data = addNode('right', '75fd', data);
data = addNode('left', '75fd', data);
data = addNode('left', '2dh3', data);
data = addNode('right', '2dh3', data);
console.log(data);
This is a tree insertion problem. First you need to find the index to the anchor node and then insert a new node at index if type is "left" or index + 1 if type is "right". Here's a solution that works for your case. Note that you need to set the right "id" using whatever id generator you have.
ds = [
{ id: "0a12", pos: 0, name: "PH1" },
{
id: "8f83",
name: "PH2",
pos: 1,
children: [
{ id: "4", pos: 0, name: "L1" },
{
id: "5",
name: "L2",
pos: 1,
children: [
{ id: "80s3", pos: 0, name: "LL1" },
{ id: "2dh3", pos: 1, name: "LL2" }
]
}
]
},
{ id: "75fd", pos: 2, name: "PH3" },
{ id: "34jg", pos: 3, name: "PH4" }
];
// Returns parent & position
function findNode(childNodes, name) {
for (var ii = 0; ii < childNodes.length; ii++) {
let child = childNodes[ii];
if (child.name == name) {
return {
childNodes: childNodes,
index: ii
};
}
if (child.children) {
let result = findNode(child.children, name);
if (result) {
return result;
}
}
}
return null;
}
function rewritePositions(childNodes) {
for (var ii = 0; ii < childNodes.length; ii++) {
childNodes[ii].pos = ii;
}
}
function insertNode(name, type) {
let searchResult = findNode(ds, name);
if (!searchResult) {
return;
}
let index = searchResult.index;
let newPosition = type === "left" ? index : index + 1;
let newNode = { id: "todo: index", pos: newPosition, name: "" };
searchResult.childNodes.splice(newPosition, 0, newNode);
rewritePositions(searchResult.childNodes);
}

Multi-select boxes not search with parent

i'm using select2 in v4.0.3
When I search through the children, for example, "sub category 1", the search appears and your parent "Category 1" is also located above.
https://ibb.co/iNAdLG
But, when I search for "Category 1" it does not show me any of the children. I have reviewed the documentation and several approaches but none has worked for me.
https://ibb.co/eVuSEb
Annex images of reference and code
$("#multisearch").select2({
language: "es",
closeOnSelect: false,
placeholder: "Comienza tu búsqueda",
data: [{
id: 0,
text: 'Linea 1',
children: [{
id: 1,
text: 'San Pablo'
},
{
id: 2,
text: 'Pajaritos'
},
{
id: 3,
text: 'Las Rejas'
},
{
id: 4,
text: 'Ecuador'
}
]
},
{
id: 5,
text: 'Linea 2',
children: [{
id: 6,
text: 'La Cisterna'
},
{
id: 7,
text: 'El Parrón'
},
{
id: 8,
text: 'Lo Ovalle'
},
{
id: 9,
text: 'Ciudad del niño'
},
{
id: 10,
text: 'Pajaritos'
}
]
},
{
id: 1,
text: 'prueba'
},
]
});
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/css/select2.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/select2.min.js"></script>
<select multiple id="multisearch" style="width:500px">
</select>
I will be attentive, thank you very much
You can achieve this by implementing custom matcher function.
Please check the example below:
$(document).ready(function(){
function customMatcher(params, data) {
data.parentText = data.parentText || "";
if ($.trim(params.term) === '') {
return data;
}
if (data.children && data.children.length > 0) {
var match = $.extend(true, {}, data);
for (var c = data.children.length - 1; c >= 0; c--) {
var child = data.children[c];
child.parentText += data.parentText + " " + data.text;
var matches = customMatcher(params, child);
if (matches == null) {
match.children.splice(c, 1);
}
}
if (match.children.length > 0) {
return match;
}
return customMatcher(params, match);
}
var original = (data.parentText + ' ' + data.text).toUpperCase();
var term = params.term.toUpperCase();
if (original.indexOf(term) > -1) {
return data;
}
return null;
}
$("#multisearch").select2({
language: "es",
closeOnSelect: false,
matcher: customMatcher,
placeholder: "Comienza tu búsqueda",
data: [{
id: 0,
text: 'Linea 1',
children: [{
id: 1,
text: 'San Pablo'
},
{
id: 2,
text: 'Pajaritos'
},
{
id: 3,
text: 'Las Rejas'
},
{
id: 4,
text: 'Ecuador'
}
]
},
{
id: 5,
text: 'Linea 2',
children: [{
id: 6,
text: 'La Cisterna'
},
{
id: 7,
text: 'El Parrón'
},
{
id: 8,
text: 'Lo Ovalle'
},
{
id: 9,
text: 'Ciudad del niño'
},
{
id: 10,
text: 'Pajaritos'
}
]
},
{
id: 1,
text: 'prueba'
},
]
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/css/select2.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/select2.min.js"></script>
<select multiple id="multisearch" style="width:500px">
</select>

Creating a JavaScript object that grabs each array item and collates it

I am trying to create a JavaScript object that grabs each services array in BusinessData, and then collates it as follows:
services: [
{
id: 1,
businessId: 1,
title: 'Brake Fluid Refresh'
},
{
id: 2,
businessId: 1,
title: 'Diagnostics'
},
{
id: 3,
businessId: 1,
title: 'Coolant Refresh'
},
{
id: 4,
businessId: 1
title: 'Oil Change'
},
{
id: 5,
businessId: 1,
title: 'MOT'
},
{
id: 6,
businessId: 1,
title: 'Summer Check'
},
{
id: 7,
businessId: 1,
title: 'Winter Check'
}
]
This is my code so far:
var businessData = [
{
id: 1,
categoryId: 1,
name: 'Japan Center Garage',
services: [
{
id: 1,
businessId: 1,
title: 'Brake Fluid Refresh'
},
{
id: 2,
businessId: 1,
title: 'Diagnostics'
}
]
},
{
id: 2,
categoryId: 1,
name: 'Rex Garage',
services: [
{
id: 3,
businessId: 1,
title: 'Coolant Refresh'
},
{
id: 4,
businessId: 1
title: 'Oil Change'
}
]
},
{
id: 3,
categoryId: 1,
name: 'Mission & Bartlett Garage',
services: [
{
id: 5,
businessId: 1,
title: 'MOT'
},
{
id: 6,
businessId: 1,
title: 'Summer Check'
},
{
id: 7,
businessId: 1,
title: 'Winter Check'
}
]
}
]
return {
getAllCategories: function () {
return categoryData;
},
getAllServices: function () {
var servicesData = {
services: []
};
for(var i = 0; i < businessData.length; i++) {
if(businessData[i].services) {
servicesData['services'].push(businessData[i].services)
}
}
return servicesData;
},
getAllBusinesses: function () {
return businessData;
},
}
}])
It doesn't work very well, and produced an array that I can't seem to access in my view after calling it from my controller:
<div class="item item-divider">
Service
</div>
<ion-list>
<ion-item ng-repeat="item in serviceList | orderBy:'title'">
<p>{{item.title}}</p>
</ion-item>
</ion-list>
<div ng-if="item !== undefined && !item.length" class="no-results">
<div class="item" style="border-top: 0px">
<p>No Results</p>
</div>
</div>
Update
serviceList is called from my the controller:
$scope.serviceList = ServicesData.getAllServices();
What is going wrong here?
var services = [];
businessData.forEach(function (business) {
Array.prototype.push.apply(services, business.services);
});
You are almost there, but you are pushing array of services to an array. By using Array.prototype.push.apply, we are instead pushing services to an array. Which eventually gives you the result you want :)
You can use lodash for that, and you better add it to your toolkit in general.
Here is how it will look with lodash:
_.flatMap(businessObject, (d) => d.services)
Or even
_.flatMap(businessObject, "services")
To make it little bit more verbose, you are doing map and then flatten:
var mapped = _.map(businessObject, "service")
var services = _.flatten(mapped)

Build Object Recursive

I have a Object which looks like the following obj.
var obj = [
{ id: 1, name: "animals" },
{ id: 2, name: "animals_cat" },
{ id: 3, name: "animals_dog" },
{ id: 4, name: "animals_weazle" },
{ id: 5, name: "animals_weazle_sand shadow weazle" },
{ id: 11, name: "fruits" },
{ id: 32, name: "fruits_banana" },
{ id: 10, name: "threes" },
{ id: 15, name: "cars" }
];
The Object should be converted into the following scheme:
var items = [
{ id: 11, name: "fruits", items: [
{ id: 32, name: "banana" }
]},
{ id: 10, name: "threes" },
{ id: 1, name: "animals", items: [
{ id: 2, name: "cat" },
{ id: 3, name: "dog" },
{ id: 4, name: "weazle", items: [
{ id: 5, name: "sand shadow weazle" }
]}
]},
{ id: 15, name: "cars" }
];
I tried a lot but unfortunately without any success. I did $.each on obj, did a split('_') on it and pushed it to items. But how can I do it for unlimited depth and push it into the right category?
I'm happy for any help.
Maybe this helps.
It works with Array.prototype.forEach for processing obj, Array.prototype.reduce for getting the right branch and Array.prototype.some for the right array element for inserting the new object.
This proposal works for sorted and consistent data.
var obj = [
{ id: 1, name: "animals" },
{ id: 2, name: "animals_cat" },
{ id: 3, name: "animals_dog" },
{ id: 4, name: "animals_weazle" },
{ id: 5, name: "animals_weazle_sand shadow weazle" },
{ id: 11, name: "fruits" },
{ id: 32, name: "fruits_banana" },
{ id: 10, name: "threes" },
{ id: 15, name: "cars" }
],
tree = [];
obj.forEach(function (a) {
var path = a.name.split('_'),
o = {};
o.id = a.id;
path.reduce(function (r, b) {
o.name = b;
r.some(function (c) {
if (c.name === b) {
c.items = c.items || [];
r = c.items;
return true;
}
});
return r;
}, tree).push(o);
});
document.write('<pre>' + JSON.stringify(tree, 0, 4) + '</pre>');
Update: Version for independent order of items.
var obj = [
{ id: 5, name: "animals_weazle_sand shadow weazle" },
{ id: 32, name: "fruits_banana" },
{ id: 1, name: "animals" },
{ id: 2, name: "animals_cat" },
{ id: 3, name: "animals_dog" },
{ id: 4, name: "animals_weazle" },
{ id: 11, name: "fruits" },
{ id: 10, name: "threes" },
{ id: 15, name: "cars" },
{ id: 999, name: "music_pop_disco_euro"}
],
tree = [];
obj.forEach(function (item) {
var path = item.name.split('_'),
o = tree;
path.forEach(function (a, i) {
var oo = { name: a, items: [] },
last = path.length - 1 === i,
found = o.some(function (b) {
if (b.name === a) {
if (last) {
b.id = item.id;
return true;
}
b.items = b.items || [];
o = b.items;
return true;
}
});
if (!found) {
if (last) {
o.push({ id: item.id, name: a });
} else {
o.push(oo);
o = oo.items;
}
}
});
});
document.write('<pre>' + JSON.stringify(tree, 0, 4) + '</pre>');

Categories