create element at index position if index does not exist in array - javascript

I have an array objects that hold an id and a name
const stages = [{
id: 1,
name: ''
}, {
id: 2,
name: ''
}, {
id: 3,
name: ''
}, {
id: 4,
name: ''
}, {
id: 5,
name: ''
}, {
id: 6,
name: ''
}, {
id: 7,
name: ''
}, {
id: 8,
name: ''
}];
Further I have an array that holds numbers.
const indexPositions = [0, 1, 2, 2, 2, 3, 2, 0];
I want to create a third array that holds arrays. Each number in distances represents the index of the current array within the array.
If the current array does not exist yet I want to create it first. Obviously I have to create new arrays until I get to this index position.
Example:
My array is empty at start. The first index position is 0 so I have to create a new array for this. The next index position is 3 so I have to create more arrays until I have 4 arrays.
All I want to do is to push the stage to its correct level index position. The result of this example would be
const levels = [
[stage1, stage8],
[stage2],
[stage3, stage4, stage5, stage7],
[stage6]
];
Currently my code looks this
$(document).ready(() => {
const levels = []; // the array containing the arrays
stages.forEach((stage, stageIndex) => {
const indexPosition = indexPositions[stageIndex];
const positionDifference = indexPosition - levels.length;
if (positionDifference > 0) {
for (let i = 0; i < positionDifference; i++) { // fill up with empty arrays
levels.push([]);
}
}
levels[indexPosition].push(stage);
});
});
I get this error Uncaught TypeError: Cannot read property 'push' of undefined and this happens because the indexPosition is out of bounds. If the positionDifference is 0 no array gets created but in the beginning the array is empty.
I tried setting levels.length to -1 if it is 0 but I still get the error if the difference is 1, I create one array at position 0 and want to access position 1.
How can I create an empty array if it does not exist?

While I do not fully understand what you want to do, checking existence of an array element is simple, one way of doing that is coercing it to boolean:
const thing=[];
function addElem(where,what){
if(!thing[where]) // <- here
thing[where]=[];
thing[where].push(what);
}
addElem(2,1);
addElem(2,2);
addElem(2,3);
addElem(5,1);
console.log(thing);
(The indices are deliberately non-continuous, because that does not matter: JavaScript arrays are sparse)

You could use a single loop and add an array for the index if not exists. Then push the wanted value.
var stages = [{ id: 1, name: '' }, { id: 2, name: '' }, { id: 3, name: '' }, { id: 4, name: '' }, { id: 5, name: '' }, { id: 6, name: '' }, { id: 7, name: '' }, { id: 8, name: '' }],
indexPositions = [0, 1, 2, 2, 2, 3, 2, 0],
result = stages.reduce((r, o, i) => {
var index = indexPositions[i];
r[index] = r[index] || []; // take default value for falsy value
r[index].push('stage' + o.id); // instead of string take object
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

You actually were very close! You have a very small issue in your code.
$(document).ready(() => {
const levels = []; // the array containing the arrays
stages.forEach((stage, stageIndex) => {
const indexPosition = indexPositions[stageIndex];
const positionDifference = indexPosition - levels.length + 1; //YOU DID NOT ADD 1 HERE
if (positionDifference > 0) {
for (let i = 0; i < positionDifference; i++) { // fill up with empty arrays
levels.push([]);
}
}
levels[indexPosition].push(stage);
});
});
When you were calculating the positionDifference, you did not add 1 causing the problem when indexPosition equaled 0 and the for loop did not run and no new arrays were pushed. Just adding one fixed the problem :-)

Related

Change values of array of objects for provided ids

how can i change the values of provided ids ? i keep getting this TypeError: Cannot set properties of undefined (setting 'select')
const data = [
{
id: 1,
select: false
},
{
id: 2,
select: true
},
{
id: 3,
select: true
},
{
id: 4,
select: false
},
{
id: 5,
select: false
}
];
const ids = [1, 2, 4];
let d = [...data];
for (let i = 0; i < d.length - 1; i++) {
const objIndex: number = d?.findIndex((obj: any) => obj.id === ids[i]);
d[objIndex].select = true;
console.log(d);
}
i want to change the boolean values of provided ids and make a new data object (same data but changed boolean values of provided ids) Need Help !
Perhaps instead of searching in the data for specific ID, search in the list of ids, it makes it simpler and depending on size of data and number of ids it might be even faster:
const data = [
{
id: 1,
select: false
},
{
id: 2,
select: true
},
{
id: 3,
select: true
},
{
id: 4,
select: false
},
{
id: 5,
select: false
}
];
const ids = [1, 2, 4];
for (let i = 0; i < data.length; i++) {
if (ids.includes(data[i].id))
data[i].select = true;
}
console.log(data);
You are iterating through d array and you're trying to access ids array with an index that does not point to any value in this array. So .findIndex is returning -1, and when you try to access it in d[objIndex] it is also undefined there because there is no element with that index.
In order to fix your code you would have to iterate first through ids array to get indices of objects with exact ids and then use that index to change the value.
Fixed code:
const data = [
{
id: 1,
select: false
},
{
id: 2,
select: true
},
{
id: 3,
select: true
},
{
id: 4,
select: false
},
{
id: 5,
select: false
}
];
const ids = [1, 2, 4];
let d = [...data];
ids.forEach((id) => {
const objIndex: number = d?.findIndex((obj: any) => obj.id === id);
// safe guard if there is no object with that id
if (objIndex === -1) {
return;
}
d[objIndex].select = true;
})
Your current for loop is doing d.length-1 iterations, so i will go out of the valid index ranges for indexes in ids. As a result, when you try and do ids[i] on a value of i that isn't an index in ids you get back undefined, which ends up with the .findIndex() being unable to find an object as there is no object that has an undefined id property. This causes the next line of code to crash as it tries to update an object in your array that doesn't exist. For your code to work your condition should be i < ids.length;.
However, assuming that data is a state value (which I've gathered here as you've tagged this as reactjs and you've cloned your data array), you shouldn't be modifying your object within data like you currently are. The main problem is that const d = [...data] only does a shallow copy of your array, so your objects are still references, which means you're modifying your state directly when you do d[objIndex].select = true which can cause issues with rerendering.
To resolve that, you could do a deep clone of your array, or instead, use .map() on your data and return a new object if it needs to be updated as shown below. The below uses the spread syntax (...) to create a new object with all the properties of the current object if its id is in ids, and then we overwrite the value of select to update it to true:
const data = [{ id: 1, select: false }, { id: 2, select: false }, { id: 3, select: false }, { id: 4, select: false }, { id: 5, select: false } ];
const ids = [1, 2, 4];
let newData = data.map(obj => ids.includes(obj.id) ? {...obj, select: true} : obj);
console.log(newData);

Loop through an array of objects, find duplicate objects by id, add their values in a single object, react js

I have a function inside of a react class component which generates properties. Since I want to be able to have duplicate properties, I've done it in a way that it is possible. However, I want those duplicate properties to be combined as a single value so that it can be displayed in the render function as a single property with a bigger value instead of 2 properties with smaller values. How can I achieve this based on the below code?
changePropertyState = () => {
let rngProperties = []
let maxProp = this.state.rarity.maxProperties;
let minProp = this.state.rarity.minProperties;
let rngCurrentPropAmount = Math.floor(Math.random() * (maxProp - minProp + 1) + minProp);
// the actual properties for an item based on the array of properties
for (let i = 0; i < rngCurrentPropAmount; i++) {
let rngNum = Math.floor(Math.random() * (itemProperties.length))
rngProperties.push(itemProperties[rngNum])
}
let proxyProperties = []
// setting the values for each property based on the min and max of that property
for (let j = 0; j < rngProperties.length; j++) {
let rngValue = this.getRandomNumber(rngProperties[j].min, rngProperties[j].max);
rngProperties[j].value = rngValue;
// creating a proxy to store unique values for each property,
let obj = {
id: rngProperties[j].id,
name: rngProperties[j].name,
min: rngProperties[j].min,
max: rngProperties[j].max,
value: rngProperties[j].value
}
proxyProperties.push(obj);
}
//setState() has an inbuilt functionality for a callback function, in which we can console.log the newly changed state
this.setState({
properties: proxyProperties
}, () => {
// console.log('propF', this.state)
});
}
An expected output of the above code is the below picture.
What I want to do is combine the 2 properties called (in this case) "Area Damage" so that only 1 property is listed but the value is 25 (again, in this case).
The itemProperties is an array of objects that have the following structure:
id: 1,
name: "intelligence",
min: 1,
max: 10,
value: 0
The rngCurrentPropAmount can be replaced with any integer for testing purposes. This is the amount of properties to be added.
The logic is to first group the array by name then merge them using reduce & summing the value. Bit tricky but working. Hope this is what was needed. The initial array has 4 elements & the final one has two. value is summed up.
const arr = [
{
id: 1, name: "intelligence", min: 1, max: 10, value: 11
},
{
id: 1, name: "intelligence", min: 1, max: 10, value: 4
},
{
id: 2, name: "dexterity", min: 1, max: 10, value: 3
},
{
id: 2, name: "dexterity", min: 1, max: 10, value: 8
}
];
//group an array by property
function groupBy(arr, property) {
return arr.reduce(function(memo, x) {
if (!memo[x[property]]) {
memo[x[property]] = [];
}
memo[x[property]].push(x);
return memo;
}, {});
}
//group by name
const grouped = groupBy(arr, "name");
const keys = Object.keys(grouped);
var output = [];
//loop keys
keys.forEach(key => {
//merge using reduce
const out = grouped[key].reduce((acc, current) => {
return {
id: current.id,
name: current.name,
min: current.min,
max: current.max,
value: acc.value + current.value
}
});
output.push(out);
});
console.log(output);

JavaScript | How can I remove an element of an array using it's value instead of it's index?

const currentMaterialsId = [1,2,3,4,5]
const materials = {
0: {
id: 1
},
1: {
id: 2
},
2: {
id: 3
},
3: {
id: 4
},
4: {
id: 5
}
}
I am trying to remove an element in the currenMaterialsId array but when I use the index of the materials object, things don't go as planned. If I use the id as the start number in splice, it still uses that number and searches for the matching index in the array instead of the value. Please help.
here's what I have at the moment.
let sortedMaterialIndex = currentMaterialsId.sort()
sortedMaterialIndex.splice(materialIndex, 1)
dispatch(removeElementCurrentMaterialsArray(selectedSheet,
sortedMaterialIndex))
ok I'm sorry it wasn't clear guys.
What I am trying to do is remove an element in currentMaterialsId that has the same value as the id in the object materials. However, when I use the id from materials as a starting number, for example
const materialId = dashboard.sheets[selectedSheet].materialProperties[materialIndex].id
currentMaterialsId.splice(materialId, 1)
it searches currentMaterialsId array for an index that matches the passed starting number(materialId), which is what I do not want.
so let's say I want to delete 2 from currentMaterialsId, could I use splice? and if I use splice, what should I pass as a starting number?
I hope this makes my question clearer.
Thanks for the responses!
What I am trying to do is remove an element in currentMaterialsId that
has the same value as the id in the object materials.
could I use splice?
You appear to be trying to do something like this:
so.js:
const materials = {
'0': { id: 1 },
'1': { id: 2 },
'2': { id: 3 },
'3': { id: 4 },
'4': { id: 5 }
};
console.log(materials);
// id from materials
let i = 1;
console.log(i);
let id = materials[i].id;
console.log(id);
function removeMaterialsId(id, materialsId) {
for (let i = 0; i < materialsId.length; i++) {
if (materialsId[i] === id) {
materialsId.splice(i--, 1);
}
}
}
let materialsId = [];
// remove materialsId elements with id from materials
console.log();
materialsId = [ 1, 2, 3, 4, 5 ];
console.log(id, materialsId);
removeMaterialsId(id, materialsId);
console.log(materialsId);
// remove materialsId elements with id from materials
console.log();
materialsId = [ 1, 2, 2, 3, 4, 2, 5 ];
console.log(id, materialsId);
removeMaterialsId(id, materialsId);
console.log(materialsId);
$ node so.js
{
'0': { id: 1 },
'1': { id: 2 },
'2': { id: 3 },
'3': { id: 4 },
'4': { id: 5 }
}
1
2
2 [ 1, 2, 3, 4, 5 ]
[ 1, 3, 4, 5 ]
2 [ 1, 2, 2, 3, 4, 2, 5 ]
[ 1, 3, 4, 5 ]
$
First off, perhaps you want to store your objects in an array, like this(?):
const materials = [
{
id: 1
},
{
id: 2
},
{
id: 3
},
{
id: 4
},
{
id: 5
}
];
Then you can remove from array using filter:
const materialToRemove = { id: 1 }
const materialsWithOneRemoved = materials
.filter(material => material.id !== materialToRemove.id);
Note that filter creates a new array, it does not change the existing array. You can however overwrite the existing array with a new one if you want to:
// materials like above, but with let instead of const
let materials = ...
const materialToRemove = { id: 1 }
materials = materials
.filter(material => material.id !== materialToRemove.id);
If you want to have your objects in an object like you have in your question, you need to first convert it to an array before you can filter. You can do that using e.g. Object.values.
Your question is far from clear, but indexOf may be a solution:
const sortedMaterialIndex = currentMaterialsId.sort();
const index = sortedMaterialIndex.indexOf(materialIndex);
if (index > -1) {
sortedMaterialIndex.splice(index, 1);
}
See How can I remove a specific item from an array?
I would recommend using the filter array function to achieve what you want.
let idToRemove = 1
let filteredMaterials = materials.filter((v) => v.id !== idToRemove);
console.log(filteredMaterials)

problems with for loop inside another for loop Javascript

I have problems in going through these two for loops, I need to get the same elements from the first array within the cycle, but the values ​​are being repeated. I know that they are repeated depending on the data of the second array.
I tried to make comparisons but I could not get the result I want.
var array = [
{
grouper: 1
},
{
grouper: 2
},
{
grouper: 3
},
{
grouper: 4
},
];
var array2 = [
{
value: 1,
grouper: 1,
status: 100
},
{
value: 2,
grouper: 2,
status: 100
},
{
value: 3,
grouper: 3,
status: 100
}
];
for(var i = 0; i<array.length; i++){
for(var j = 0; j<array2.length; j++){
if(array2[j].grouper == array[i].grouper){
console.log(array[i].grouper+'-'+array2[j].value);
}
}
}
This is the result I want, I need all the groupers from the first array and the values from the second array:
1-1
2-2
3-3
4-
The grouper 4, does not have value, but I need to show it.
I need the second array because I'm going to compare with the data from the second array
I do not know if I am doing the process wrong. I hope you can help me.
You could simply track if there was a match (variable shown), and if there were not any, display a "half" line:
var array = [{grouper: 1},{grouper: 2},{grouper: 3},{grouper: 4},];
var array2 = [
{value: 1, grouper: 1, status: 100},
{value: 2, grouper: 2, status: 100},
{value: 3, grouper: 3, status: 100}
];
for(var i = 0; i<array.length; i++){
var shown=false;
for(var j = 0; j<array2.length; j++){
if(array2[j].grouper == array[i].grouper){
console.log(array[i].grouper+'-'+array2[j].value);
shown=true;
}
}
if(!shown){
console.log(array[i].grouper+"-");
}
}
First of all, with the example you provided I believe you want to get back:
1,2,3
There is no 4th object inside of array2, so your conditional (array2[j].grouper == array[i].grouper will never evaluate to true.
The question here is whether you are always comparing the same indexes? In this example, you're comparing array[0] to array2[0] to see if grouper in array equals grouper in array2... that's it????
In that case you just do one loop:
for (var i = 0; i < array.length; i++) {
if (array[i].grouper == array2[i].grouper) {
console.log(array[i].grouper+'-'+array2[j].value);
}
}
#FabianSierra ... with your provided example one just needs to handle the not fulfilled if clause/condition in the most inner loop.
A more generic approach additionally might take into account changing field names (keys). Thus a function and Array.reduce / Array.find based approach provides better code reuse. An example implementation then might look similar to that ...
var array = [{ // in order.
grouper: 1
}, {
grouper: 2
}, {
grouper: 3
}, {
grouper: 4
}];
var array2 = [{ // not in the order similar to `array`.
value: 22,
grouper: 2,
status: 200
}, {
value: 33,
grouper: 3,
status: 300
}, {
value: 11,
grouper: 1,
status: 100
}];
function collectRelatedItemValuesByKeys(collector, item) {
var sourceKey = collector.sourceKey;
var targetKey = collector.targetKey;
var targetList = collector.targetList;
var resultList = collector.result;
var sourceValue = item[sourceKey];
var targetValue;
var relatedItem = targetList.find(function (targetItem) {
return (targetItem[sourceKey] === sourceValue);
});
if (typeof relatedItem !== 'undefined') {
targetValue = relatedItem[targetKey];
} else if (typeof targetValue === 'undefined') {
targetValue = ''; // `relatedItem` does not exist.
}
resultList.push([sourceValue, targetValue].join('-'));
return collector;
}
var resultList = array.reduce(collectRelatedItemValuesByKeys, {
sourceKey: 'grouper',
targetKey: 'value',
targetList: array2,
result: []
}).result;
console.log('resultList : ', resultList);
resultList = array.reduce(collectRelatedItemValuesByKeys, {
sourceKey: 'grouper',
targetKey: 'status',
targetList: array2,
result: []
}).result;
console.log('resultList : ', resultList);
.as-console-wrapper { max-height: 100%!important; top: 0; }

Manipulating arrays in JavaScript to remove repeat objects and total them up

How would I take the following array in JavaScript
locationList = [{
id: 1,
title: "Loughborough"
}, {
id: 5,
title: "Corby"
}, {
id: 2,
title: "Derby"
}, {
id: 2,
title: "Derby"
}, {
id: 2,
title: "Derby"
}];
and convert it into something like this:
locationList = [{
id: 1
title: "Loughborough",
count: 1
}, {
id: 5
title: "Corby",
count: 1
}, {
id: 2
title: "Derby",
count: 3
}];
wherein all the titles are totalled up.
One of many solutions:
var newl = [];
locationList.forEach(function(o) {
if (newl[o.id] == undefined) {
o.count = 1;
newl[o.id] = o;
} else {
newl[o.id].count += 1;
}
});
// if you want a trimmed array (with length = 3)
var trimedArray = newl.filter(function(n){ return n != undefined });
Loop through the elements, create a new array, put an element into the new array if the element is not yet in.
Edit:
Otherwise, if it exists, just add 1 to the count in the new array, and then use the new array instead of the old.
As I have said in comments :
Create new array, loop through each element, if element.title is not in new array, add to array, if it is add to count in specific index

Categories

Resources