Array forEach overwriting value of object on last iteration - javascript

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);

Related

Type script- React native : How to modify json response?

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]);

Apply every function to forEach javascript

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`);
}
});

Format a nested object

I have a javascript variable with the following structure
var recurse = {
level_0: [
{
level_1 : [
{
level_2_0 : [
{
level_3_0: {
valid: true,
available: false
}
}, {
level_3_1 : {
valid: true,
available: true
}
}]
}, {
level_2_1 : [
{
level_3_0: {
valid: true,
available: false
}
}, {
level_3_1 : {
valid: true,
available: true
}
}]
}]
}]
}
Final required output structure
var recurse = {
name: "level_0",
property: [
{
name: "level_1",
property: [
{
name: "level_2_0",
property: [
{
name: "level_3_0",
property: {
valid: true,
available: false
}
}, {
name: "level_3_1",
property: {
valid: true,
available: true
}
}]
}, {
name: "level_2_1",
property: [
{
name: "level_3_0",
property: {
valid: true,
available: false
}
}, {
name: "level_3_1",
property: {
valid: true,
available: true
}
}]
}]
}]
}
Facing problem with cloning and updating the structure for this nested object using generic methods.
How can I achieve the required final object structure using simple javascript or reactjs properties.
Which is the most appropriate method to clone a javascript object?
Note: the object names- level_0, level_1 level_2_0 could be random or dynamic.
You can write a simple recursive function that recurses on the item if the item value is an array like below
var recurse = {
level_0: [
{
level_1 : [
{
level_2_0 : [
{
level_3_0: {
valid: true,
available: false
}
}, {
level_3_1 : {
valid: true,
available: true
}
}]
}, {
level_2_1 : [
{
level_3_0: {
valid: true,
available: false
}
}, {
level_3_1 : {
valid: true,
available: true
}
}]
}]
}]
}
function format(data) {
return Object.entries(data).map(([key, value]) => {
if(Array.isArray(value))
return {
name: key,
property: [].concat(...value.map(item => format(item)))
}
return {
name: key,
property: value
}
})
}
console.log(format(recurse));
You can transform it with something like this:
const transform = (tree) =>
Object (tree) === tree
? Object .entries (tree) .map (([name, properties]) => ({
name,
properties: Array .isArray (properties) ? properties .map (transform) : properties
})) [0] // This is a strage thing to do with an object! The original data structure is odd.
: tree
var recurse = {level_0: [{level_1: [{level_2_0: [{level_3_0: {valid: true, available: false}}, {level_3_1: {valid: true, available: true}}]}, {level_2_1: [{level_3_0: {valid: true, available: false}}, {level_3_1: {valid: true, available: true}}]}]}]}
console .log (transform (recurse))
.as-console-wrapper {min-height: 100% !important; top: 0}
But the data structure you start with is quite odd. Each level of nesting except the innermost can only have a single property. Is that really how your data comes to you? It's a strange use of objects. (Then again, you are asking about how to reforat it...)
The [0] on the sixth line of the above deals with that oddity of the input.

ES5 only: return value when match is found after iterating over various values in an array

I am writing a function to return an id based on a few various things aligning. The code mostly works except that because there is no break; for a forEach. I believe I need to use a different filter or find option on the array.
function getDefaultId(prod) {
var defaultId;
prod.images.forEach( function(image) {
defaultId = image.isPrimary ? image.id : undefined;
});
return defaultId;
}
var prod.images = [
0: {
isPrimary: false,
id: 1234
},
1: {
isPrimary: true,
id: 1235
},
2: {
isPrimary: false,
id: 1236
}
]
Essentially I'm trying to return the corresponding id for isPrimary. The result should be 1236 but I'm getting undefined because the forEach is not breaking and thus is resetting the variable to undefined on the next iteration.
You could use Array#some and exit early.
function getDefaultId(prod) {
var defaultId;
prod.images.some(function (image) {
if (image.isPrimary) {
defaultId = image.id;
return true;
}
});
return defaultId;
}
var prod = { images: [{ isPrimary: false, id: 1234 }, { isPrimary: true, id: 1235 }, { isPrimary: false, id: 1236 } ]};
console.log(getDefaultId(prod));
var images = [
{
isPrimary: false,
id: 1234
},
{
isPrimary: true,
id: 1235
},
{
isPrimary: false,
id: 1236
}
]
let result = images.filter(img => img.isPrimary === true)
console.log(result)
You could use .filter() and to get an array of objects which have isPrimary as true, and then return the id of the first item found like so:
function getDefaultId(prod) {
var primaryImg = prod.images.filter(function(img) {
return img.isPrimary;
});
return primaryImg[0].id;
}
var prod = {};
prod.images = [{isPrimary: false, id: 1234 }, {isPrimary: true, id: 1235}, {isPrimary: false, id: 1236}];
console.log(getDefaultId(prod));
Give it a try using map.
var images = [
{
isPrimary: false,
id: 1234
},
{
isPrimary: true,
id: 1235
},
{
isPrimary: false,
id: 1236
}
];
function getDefaultId() {
var defaultId;
images.map(function (image) {
if (image.isPrimary) {
defaultId = image.id;
}
});
return defaultId;
}
console.log(getDefaultId());

How to push the data with specific status set?

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);

Categories