Thats my object tabs:
tabs: [
{
visible: true,
title: 'tab 0',
products: [{id:1, send: true, delivered: true}, {id:1, send: true, delivered: true}],
},
{
visible: true,
title: 'tab 1',
products: [{id:11, send: true, delivered: false}, {id:21, send: true, delivered: true}],
}
],
const allDerivered = (product) => product.delivered;
options.tabs.forEach(function (projectProducts){
if (projectProducts.products.every(allDerivered)){
statusTabs = 'DELIVERED';
}
});
Like the first products tabs are allDerivered it doesn't iterate through all the tabs object. How I can apply the allDerivered function to all the tabs?
I'm going to guess that you want to set statusTabs to "DELIVERED" if all products in all tabs are delivered, and not if any product in any tab is not delivered. If so, you can do that in a few ways.
You could use nested every calls:
if (options.tabs.every(({products}) => products.every(({delivered}) => delivered)) {
statusTabs = "DELIVERED";
}
Or nested loops with a label and a directed break:
let allDelivered = true;
outer: for (const {products} of options.tabs) {
for (const {delivered} of products) {
if (!delivered) {
allDelivered = false;
break outer;
}
}
}
if (allDelivered) {
statusTabs = "DELIVERED";
}
That's less cumbersome if you're also assigning to statusTabs in the false case:
statusTabs = "DELIVERED";
outer: for (const {products} of options.tabs) {
for (const {delivered} of products) {
if (!delivered) {
statusTabs = "UNDELIVERED";
break outer;
}
}
}
I'm going to make that assumption for the rest of the answer.
I didn't use allDelivered in the above because the name doesn't match what the function does (it only checks if one product is delivered, not if the all are) and because what the function does is just a single check of a property value which is just as clear done inline. But if you wanted a reusable function that does that check, you could have one:
const isDelivered = ({delivered}) => delivered;
Then the two code blocks above would be:
if (options.tabs.every(({products}) => products.every(isDelivered)) {
statusTabs = "DELIVERED";
}
Or nested loops with a label and a directed break:
statusTabs = "DELIVERED";
outer: for (const {products} of options.tabs) {
for (const product of products) {
if (isDelivered(product)) {
statusTabs = "UNDELIVERED";
break outer;
}
}
}
You could go further and have an allProductsDelivered function for tabs:
const allProductsDelivered = ({products}) => products.every(isDelivered);
Then the nested every would be:
if (options.tabs.every(allProductsDelivered)) {
statusTabs = "DELIVERED";
}
And the loops version would be:
statusTabs = "DELIVERED";
for (const {products} of options.tabs) {
if (!allProductsDelivered(tab)) {
statusTabs = "UNDELIVERED";
break;
}
}
I'm guessing you want something like this:
const tabs = [{
visible: true,
title: 'tab 0',
products: [{
id: 1,
send: true,
delivered: true
}, {
id: 1,
send: true,
delivered: true
}],
},
{
visible: true,
title: 'tab 1',
products: [{
id: 11,
send: true,
delivered: false
}, {
id: 21,
send: true,
delivered: true
}],
}
];
const isNotDelivered = (product) => product.delivered == false;
tabs.forEach(tab => {
const areAllProductsDelivered = !tab.products.find(isNotDelivered);
if (areAllProductsDelivered) {
console.log(`${tab.title} is completely delivered`);
} else {
console.log(`${tab.title} is NOT completely delivered`);
}
});
Related
What is the correct way to modify json response ,
My goal is to display all the MaintroomName belonging to the same Plsectn
This is the function that needs to modify to get the same structure
which I mentioned below that I am interested in reaching.
useEffect(() => {
BtpBridgeModule.loadDataFromSdk(
'GushSet',
[],
{ PlantID: userData.plant, LocationID: userData.LocationID },
undefined,
0,
).then(function (dataResolved) {
let aResults = JSON.parse(dataResolved).value;
});
}, [userData.LocationID, userData.plant]);
The json look like this :
[
{
"Maintroom":"221",
"MaintroomName":"gogi",
"Plsectn":"22",
"PlsectnName":"pardehan"
},
{
"Maintroom":"222",
"MaintroomName":"nahaleymenash",
"Plsectn":"22",
"PlsectnName":"pardehan"
},
{
"Maintroom":"231",
"MaintroomName":"gvul",
"Plsectn":"23",
"PlsectnName":"meshulash"
},
{
"Maintroom":"232",
"MaintroomName":"daro",
"Plsectn":"23",
"PlsectnName":"meshulash"
},
]
I wanna change it to this structure :
[
{
title: PlsectnName,
checked: false,
data: [
{ key: MaintroomName, value: false, checked: false },
{ key: MaintroomName, value: false, checked: false },
{ key: MaintroomName, value: false, checked: false },
{ key: MaintroomName, value: false, checked: false },
],
},
{
title: PlsectnName,
checked: false,
data: [
{ key: MaintroomName, value: false, checked: false },
{ key: MaintroomName, value: false, checked: false },
{ key: MaintroomName, value: false, checked: false },
],
},
]
Note - each Plsectn can have a dynamic number of MaintroomName.
Algorithm to sort your data
// Your response data
const data = [
{
"Maintroom":"221",
"MaintroomName":"gogi",
"Plsectn":"22",
"PlsectnName":"pardehan"
},
{
"Maintroom":"222",
"MaintroomName":"nahaleymenash",
"Plsectn":"22",
"PlsectnName":"pardehan"
},
{
"Maintroom":"231",
"MaintroomName":"gvul",
"Plsectn":"23",
"PlsectnName":"meshulash"
},
{
"Maintroom":"232",
"MaintroomName":"daro",
"Plsectn":"23",
"PlsectnName":"meshulash"
},
];
// Variable to track duplicate keys (PlsectnName)
let keys = [];
// Result after sorting the data
let result = [];
// Algorithm to sort the data
data.forEach((obj) => {
if(!keys.includes(obj.PlsectnName)){
result.push({
title: obj.PlsectnName,
checked: false,
data: [
{ key: obj.MaintroomName, value: obj.Maintroom, checked: false }
]
});
keys.push(obj.PlsectnName);
}
else {
result.forEach((subObj,index) => {
if(subObj.title == obj.PlsectnName){
subObj.data = [...subObj.data, { key: obj.MaintroomName, value: obj.Maintroom, checked: false }]
result[index] = subObj;
}
});
}
})
// Log the result
console.log(result)
(Note: If you want to set the value as false then change value: obj.Maintroom to value: false)
Implementing the Algorithm in your useEffect function.
// Algorithm as function to sort your data
const sortData = (data) => {
// Variable to track duplicate keys (PlsectnName)
let keys = [];
// Result after sorting the data
let result = [];
// Algorithm to sort the data
data.forEach((obj) => {
if(!keys.includes(obj.PlsectnName)){
result.push({
title: obj.PlsectnName,
checked: false,
data: [
{ key: obj.MaintroomName, value: obj.Maintroom, checked: false }
]
});
keys.push(obj.PlsectnName);
}
else {
result.forEach((subObj,index) => {
if(subObj.title == obj.PlsectnName){
subObj.data = [...subObj.data, { key: obj.MaintroomName, value: obj.Maintroom, checked: false }]
result[index] = subObj;
}
});
}
})
// return the result
return result;
}
// Your function
useEffect(() => {
BtpBridgeModule.loadDataFromSdk(
'GushSet',
[],
{ PlantID: userData.plant, LocationID: userData.LocationID },
undefined,
0,
).then(function (dataResolved) {
let aResults = JSON.parse(dataResolved).value;
// Added code
let sortedResult = sortData(aResults)
// Here sortedResult is your final data
});
}, [userData.LocationID, userData.plant]);
I'm looping through an array of file names, splitting the names, and storing the data in an object. The two file names for testing purposes are identical except for the week "number" which should create two separate weeks. The problem is that the first entry is being overwritten by the last iteration so I end up with an entry for week 2 only.
The code:
const planList = [
'military_greekHero_achilles_week_1.htm',
'military_greekHero_achilles_week_2.htm'
];
var _completePlan = {};
planList.forEach(_plan => {
// Pull data from the file name formated: target_series_title_overview/week_weekNum.htm
let _planPieces = _plan.split('.')[0].split('_'),// Drop the .htm
_planTarget = _planPieces[0],
_planSeries = _planPieces[1],
_planTitle = _planPieces[2],
_planOverview = _planPieces[3],
_planWeek = _planPieces[4];
_planOverview = _planOverview == 'overview' ? true : false;
// Start Building Plan Object
_completePlan[_planTitle] = {
info: {},
weeks: {}
}
// _planWeek logs 1 and 2 while iterating but entry for .weeks.1 is overwritten with .weeks.2
_completePlan[_planTitle].weeks[_planWeek] = {
sn: { inactive: true },
mo: { inactive: true },
tu: { inactive: true },
we: { inactive: true },
th: { inactive: true },
fr: { inactive: true },
st: { inactive: true }
}
});
console.log(_completePlan);
});
I feel like I'm missing something simple... any ideas?
You just need to check if the object already exists before you try and create it (thus overwriting the previous):
if (!_completePlan.hasOwnProperty(_planTitle)) {
_completePlan[_planTitle] = {
info: {},
weeks: {}
}
}
Also I added in a little restructuring statement that helps reduce some code:
let [_planTarget, _planSeries, _planTitle, _planO, _planWeek] = _plan.split('.')[0].split('_'), // Drop the .htm
_planOverview = _planO === 'overview' ? true : false;
const planList = [
'military_greekHero_achilles_week_1.htm',
'military_greekHero_achilles_week_2.htm'
];
var _completePlan = {};
planList.forEach(_plan => {
// Pull data from the file name formated: target_series_title_overview/week_weekNum.htm
let [_planTarget, _planSeries, _planTitle, _planO, _planWeek] = _plan.split('.')[0].split('_'), // Drop the .htm
_planOverview = _planO === 'overview' ? true : false;
// Start Building Plan Object
if (!_completePlan.hasOwnProperty(_planTitle)) {
_completePlan[_planTitle] = {
info: {}, weeks: {}
}
}
_completePlan[_planTitle].weeks[_planWeek] = {
sn: { inactive: true},
mo: { inactive: true},
tu: { inactive: true},
we: { inactive: true},
th: { inactive: true},
fr: { inactive: true},
st: { inactive: true}
}
});
console.log(_completePlan);
You are resetting the whole of _completePlan[_planTitle] with each iteration. So the 1 or 2 objects inside the weeks object are not getting "overridden" per se, but rather their parent's parent object is being reset to {info: {}, weeks: {}}.
So you need to set the weeks object to itself if it exists, and only if it doesn't to a blank object.
Here is how you would do that:
const planList = [
'military_greekHero_achilles_week_1.htm',
'military_greekHero_achilles_week_2.htm'
];
var _completePlan = {};
planList.forEach(_plan => {
// Pull data from the file name formated: target_series_title_overview/week_weekNum.htm
let _planPieces = _plan.split('.')[0].split('_'),// Drop the .htm
_planTarget = _planPieces[0],
_planSeries = _planPieces[1],
_planTitle = _planPieces[2],
_planOverview = _planPieces[3],
_planWeek = _planPieces[4];
_planOverview = _planOverview == 'overview' ? true : false;
// Start Building Plan Object
// _planWeek logs 1 and 2 while iterating but entry for .weeks.1 is overwritten with .weeks.2
_completePlan[_planTitle] = _completePlan[_planTitle] || {};
_completePlan[_planTitle].info = _completePlan[_planTitle].info || {};
_completePlan[_planTitle].weeks = _completePlan[_planTitle].weeks || {};
_completePlan[_planTitle].weeks[_planWeek] = {
sn: { inactive: true },
mo: { inactive: true },
tu: { inactive: true },
we: { inactive: true },
th: { inactive: true },
fr: { inactive: true },
st: { inactive: true }
}
});
console.log(_completePlan);
I have a config.ts file with following content:
export const keyOrders: {} = {
"aa": { active: true, order: 0 },
"bb": { active: true, order: 1 },
"cc": { active: true, order: 2 },
"dd": { active: true, order: 3 },
"ee": { active: false, order: 4 },
"ff": { active: true, order: 5 }
};
I am trying to push to array if active is only true. I have tried following code which is pushing key if active is true but if active is false then it is returning "undefined"
public keys = [];
public keyOrders = keyOrders;
ngOnInit() {
this.keys = Object.entries(this.keyOrders).map((a: any) => {
if(a[1].active == 'true') {
return a[0];
}
});
}
Use filter followed by map.
If you want to sort the items based on their order property, use sort before map.
filter will only keep items passing the predicate, in this case a truthy active property. Then map will map this array to the keys.
In your case, using map will give you an array of the same length, you must filter it first.
type Order = { active: boolean, order: number };
const keyOrders: { [key: string]: Order } = {
"aa": { active: true, order: 0 },
"bb": { active: true, order: 1 },
"cc": { active: true, order: 2 },
"dd": { active: true, order: 3 },
"ee": { active: false, order: 4 },
"ff": { active: true, order: 5 }
}
this.keys = Object.entries(this.keyOrders)
.filter(([_, val]) => val.active)
.sort((a, b) => a[1].order - b[1].order)
.map(([key, _]) => key);
For the types to work out, Object.entries() must be recognized by Typescript, for this, add "lib": [ "es2017.object" ] in your tsconfig.json file.
Here is a JavaScript demo (types stripped):
const keyOrders = {
"aa": { active: true, order: 0 },
"bb": { active: true, order: 1 },
"cc": { active: true, order: 2 },
"dd": { active: true, order: 3 },
"ee": { active: false, order: 4 },
"ff": { active: true, order: 5 }
};
const keys = Object.entries(keyOrders)
.filter(([_, val]) => val.active)
.sort((a, b) => a[1].order - b[1].order)
.map(([key, _]) => key);
console.log(keys);
I have an array of objects that have deeply nested children and sometimes children within children. I am attempting to handle this recursively, but I am getting stuck.
The goal of the function is to return a single data object that matches the id.
My Data looks like this:
data: [
{
id: 'RAKUFNUBNY00UBZ40950',
name: 'Grade 1 Cover',
activityId: 'RAKUFNUBNY00UBZ40950',
nodeType: 'activity',
suppressed: false,
hidden: false
},
{
children: [
{
id: 'SLWDYEQHTZAFA3ALH195',
name: 'Build Background Video',
activityId: 'SLWDYEQHTZAFA3ALH195',
nodeType: 'activity',
suppressed: false,
hidden: false,
assetReference: {
referenceId: 'UWFHA5A1E0EGKCM0W899',
assetType: 'image'
}
},
{
children: [
{
id: 'HQUCD2SSRKMYC2PJM636',
name: 'Eat or Be Eaten Splash Card',
activityId: 'HQUCD2SSRKMYC2PJM636',
nodeType: 'activity',
suppressed: false,
hidden: true
},
{
children: [
{
id: 'ZDTWEZFL13L8516VY480',
name: 'Interactive Work Text: Eat or Be Eaten',
activityId: 'ZDTWEZFL13L8516VY480',
nodeType: 'activity',
suppressed: false,
hidden: true,
defaultLaunchMode: 'modal'
}
],
My attempt at solving this is like this:
findNode(id, currentNode) {
console.log('id', id);
console.log('findNode', currentNode);
var i, currentChild, result, counter;
counter = 0;
console.log('first conditional statement', currentNode);
if (id && currentNode.id === id) {
return currentNode[0];
} else {
counter++;
// Use a for loop instead of forEach to avoid nested functions
// Otherwise "return" will not work properly
console.log('counter', counter);
console.log('currentNode', currentNode[counter]);
console.log('currentNode Children', currentNode.children);
for (i = counter; i < currentNode.children.length; i += 1) {
console.log(currentNode[i].children[i]);
currentChild = currentNode[i].children[i];
// Search in the current child
result = this.findNode(id, currentChild);
// Return the result if the node has been found
if (result !== false) {
return result;
}
}
// The node has not been found and we have no more options
return false;
}
}
The code above fails because I having an extremely difficult time keeping track of a counter to loop through everything.
I also added a sample picture of my data output to give you a better example of how my data is structured. Any help will be greatly appreciated.
You shouldn't need a counter to locate a single node with a matching id. Try this simpler approach:
function findNode (id, array) {
for (const node of array) {
if (node.id === id) return node;
if (node.children) {
const child = findNode(id, node.children);
if (child) return child;
}
}
}
It will return undefined if there is no match.
To avoid the need for manual iteration, you might consider using an array method like reduce instead - return the accumulator if it's truthy (that is, an object was found already), or return the object being iterated over if the ID matches, or recursively iterate over the object's children to find a match.
const data=[{id:'RAKUFNUBNY00UBZ40950',name:'Grade 1 Cover',activityId:'RAKUFNUBNY00UBZ40950',nodeType:'activity',suppressed:!1,hidden:!1},{children:[{id:'SLWDYEQHTZAFA3ALH195',name:'Build Background Video',activityId:'SLWDYEQHTZAFA3ALH195',nodeType:'activity',suppressed:!1,hidden:!1,assetReference:{referenceId:'UWFHA5A1E0EGKCM0W899',assetType:'image'}},{children:[{id:'HQUCD2SSRKMYC2PJM636',name:'Eat or Be Eaten Splash Card',activityId:'HQUCD2SSRKMYC2PJM636',nodeType:'activity',suppressed:!1,hidden:!0},{children:[{id:'ZDTWEZFL13L8516VY480',name:'Interactive Work Text: Eat or Be Eaten',activityId:'ZDTWEZFL13L8516VY480',nodeType:'activity',suppressed:!1,hidden:!0,defaultLaunchMode:'modal'}],}],}],}]
function findId(id, arr) {
return arr.reduce((a, item) => {
if (a) return a;
if (item.id === id) return item;
if (item.children) return findId(id, item.children);
}, null);
}
console.log(findId('HQUCD2SSRKMYC2PJM636', data));
If your ids are unique and finding an object by id is a common task, you might want to consider creating a lookup object to improve performance. Creating the lookup object is an O(n) task; afterwards, looking up an object by id is O(1).
const data = [ { id: 'RAKUFNUBNY00UBZ40950', name: 'Grade 1 Cover', activityId: 'RAKUFNUBNY00UBZ40950', nodeType: 'activity', suppressed: false, hidden: false }, { children: [ { id: 'SLWDYEQHTZAFA3ALH195', name: 'Build Background Video', activityId: 'SLWDYEQHTZAFA3ALH195', nodeType: 'activity', suppressed: false, hidden: false, assetReference: { referenceId: 'UWFHA5A1E0EGKCM0W899', assetType: 'image' } }, { children: [ { id: 'HQUCD2SSRKMYC2PJM636', name: 'Eat or Be Eaten Splash Card', activityId: 'HQUCD2SSRKMYC2PJM636', nodeType: 'activity', suppressed: false, hidden: true }, { children: [ { id: 'ZDTWEZFL13L8516VY480', name: 'Interactive Work Text: Eat or Be Eaten', activityId: 'ZDTWEZFL13L8516VY480', nodeType: 'activity', suppressed: false, hidden: true, defaultLaunchMode: 'modal' } ] } ] } ] } ];
const lookup = {};
const registerIds = a => {
a.forEach(o => {
if ('id' in o) {
lookup[o.id] = o;
} else if ('children' in o) {
registerIds(o.children)
}
});
}
registerIds(data);
console.log(lookup)
Sorry for my two cents, just want to add a universal method that includes nested arrays
const cars = [{
id: 1,
name: 'toyota',
subs: [{
id: 43,
name: 'supra'
}, {
id: 44,
name: 'prius'
}]
}, {
id: 2,
name: 'Jeep',
subs: [{
id: 30,
name: 'wranger'
}, {
id: 31,
name: 'sahara'
}]
}]
function searchObjectArray(arr, key, value) {
let result = [];
arr.forEach((obj) => {
if (obj[key] === value) {
result.push(obj);
} else if (obj.subs) {
result = result.concat(searchObjectArray(obj.subs, key, value));
}
});
console.log(result)
return result;
}
searchObjectArray(cars, 'id', '31')
searchObjectArray(cars, 'name', 'Jeep')
I hope this helps someone
I am using angular and I have an array of Objects let's say Item(SmallItem, Bollean, Boolean) and in my code I add elements to this array by pushing, like for example:
this.Items.push(new Item(smallItem, false, true));
However, I would like to push an Item if and only if item with the same smallItem does not exist in Items. How do I go about it?
You can simply go back to the basic and do something along thoses lines :
const itemToAdd = new Item(smallItem, false, true);
if(this.Items.findIndex((item) => item.smallItem === itemToAdd.smallItem) < 0) {
this.Items.push(itemToAdd);
}
or if you don't want to create the Item if it is not added :
if(this.Items.findIndex((item) => item.smallItem === smallItemToAdd) < 0) {
this.Items.push(new Item(smallItemToAdd, false, true););
}
You can remove the items afterward with smallItem in it like
Items.forEach((index,item)=>{
if(item.smallItem){
Items.splice(index,1)
)
an other approach could be:
var find = false;
for (let item of Items) {
// same smallItem value
if(item.smallItem) {
find = true;
}
}
if(!find) {
this.Items.push(new Item(smallItem, false, true));
}
Give this a try:
let items = [
{ name: 'Item 1', booleanA: true, booleanB: true },
{ name: 'Item 2', booleanA: true, booleanB: true },
{ name: 'Item 3', booleanA: true, booleanB: true },
{ name: 'Item 4', booleanA: true, booleanB: true },
{ name: 'Item 5', booleanA: true, booleanB: true }
];
function addItemIfNotAlreadyPresent(itemToAdd) {
let itemAlreadyExist = items.find(
item => item.name === itemToAdd.name && item.booleanA === itemToAdd.booleanA && item.booleanB === itemToAdd.booleanB
);
if(!itemAlreadyExist) {
items.push(itemToAdd);
}
}
let itemAlreadyPresent = { name: 'Item 1', booleanA: true, booleanB: true };
addItemIfNotAlreadyPresent(itemAlreadyPresent);
console.log('Items after trying to add itemAlreadyPresent: ', items);
let itemNotPresent = { name: 'Item 6', booleanA: true, booleanB: true };
addItemIfNotAlreadyPresent(itemNotPresent);
console.log('Items after trying to add itemNotPresent: ', items);