This is the section of code I am dealing with,
This is my hook
const [array, setArray] = useState(
JSON.parse(localStorage.getItem("notes")) ?? []
);
And this is the function,
const save = () => {
let info = one.current.value;
console.log(newIndex, " and ", info);
console.log("this si array element -> ", array[newIndex - 1]);
array[newIndex - 1] = info;
if (newIndex !== undefined) {
setArray((e) => {
return e.map((e1, i) => {
if (i + 1 === newIndex) {
return [...e ];
}
});
});
}
};
info has the correct input I want to update to and newIndex have the index of the element I wish to update.
Can you suggest to me what I need to change in this section, this is part of the above function,
setArray((e) => {
return e.map((e1, i) => {
if (i + 1 === newIndex) {
return [...e ];
}
});
To update your array you do not need to map anything. You only need to copy the array, modify the copied array at the right index and return the copied array. React will take care of the rest.
It's important, that you copy the array, else react won't notice the change. useState only remembers the array's address in memory.. Only through a new array with a new address will useState actually fire.
Here is my take on what I think your save() function should look like:
const save = () => {
let info = one.current.value;
if (newIndex !== undefined) {
console.log(newIndex, " and ", info);
console.log("this is array element -> ", array[newIndex - 1]);
setArray((e) => {
let temp = [...e];
temp[newIndex - 1] = info;
return temp;
});
}
};
Tried the code in codesandbox and I think this is what you want.
Related
I try to insert a string into a particular index of object if condition is true inside a forloop but its not inserting of some reason. I tried to use push and append and splice but splice just inserting entire string as an new object into the array and i need it to just append to existing object. Any ideas how to make it work?
Data looks like that:
const [concerts, setConcerts] = useState([]);
const [tickets, setTickets] = useState([]);
const [limit, setLimit] = useState(25);
const navigate = useNavigate();
const [button, setButton] = useState(false);
const [array, setArray] = useState([]);
//Raw JSON Date example: "2023-02-08T23:15:30.000Z"
let currentDate = new Date().toJSON().slice(0, 10);
const json = { available: "true" };
useEffect(() => {
const loadConcerts = async () => {
const resConcerts = await axios.get("/data/concerts");
const resTickets = await axios.get("/data/tickets");
let table = [];
setTickets(resTickets.data);
// getting all concerts above today
const filteredData = resConcerts.data.filter((concert) => {
return concert.datum >= currentDate;
});
filteredData.forEach((element) => {
table.push(element);
// table.splice(10, 0, { status: "available" });
});
setArray(table);
for (let i = 0; i < resTickets.data.length; i++) {
for (let j = 0; j < filteredData.length; j++) {
if (
resTickets.data[i].concertid == filteredData[j].id &&
resTickets.data[i].booked == 0
) {
table.push({ status: "avaiable" });
// table.splice(10, 0, { status: "available" });
}
}
}
setArray(table);
// filteredData.forEach((concert) => {
// for (const ticket of tickets) {
// if (concert.id == ticket.concertid && ticket.booked == 0) {
// table.push(json);
// }
// }
// });
setConcerts(
filteredData.sort((a, b) =>
a.datum > b.datum ? 1 : a.datum < b.datum ? -1 : 0
)
);
};
console.log("from use effect: " + array.length);
loadConcerts();
}, []);
After using splice method:
Update
Problem is solved. I used Object.assign() helped to append string to existing object in array. Actually i had to insert another object, not a single variable.
The problem is you are trying to push a string "available" into an array-of-objects.
Here you see the object with a property datum:
const filteredData = resConcerts.data.filter((concert) => {
return concert.datum >= currentDate;
});
Yet below when you push, you are not pushing an object into the array which is problematic. It should probably be something like this but you have to verify:
Instead of this:
filteredData.push("available");
Domething like this:
filteredData.push({ datum: '', status: 'available' );
I don't know what your data object is but it's an object not a string you need to add to that array.
The looping twice is likely from React 18 New Strict Mode Behaviors. It intentionally unmounts/remounts components to fire your useEffect calls twice - so that you can identify problematic side effects. If you remove <StrictMode> or run in production that double-looping should not occur.
Problem solved. Push() neither splice() method didn't helped. What helped me to append my object to another object without changing the data was Object.assign() function
for (let i = 0; i < resTickets.data.length; i++) {
for (let j = 0; j < filteredData.length; j++) {
if (
resTickets.data[i].concertid == filteredData[j].id &&
resTickets.data[i].booked == 0
) {
Object.assign(filteredData[j], obj);
}
}
}
trying to find the best way to check for triplicates values inside an array of strings.
I found many stackoverflow solutions for duplicates values which is not the case in here.
This is the farest i could get with solving this and I am not sure if it is the correct way:
const array = [
"peace",
"peace",
"Vrede",
"Patz",
"Salam",
"paz",
"Salam",
"Salam"
];
const findTriplicates = (param) => {
let counts = {};
for (let i = 0; i < param.length; i++) {
if (counts[param[i]]) {
counts[param[i]] += 1;
} else {
counts[param[i]] = 1;
}
}
for (let i in counts) {
if (counts[i] === 3) {
console.log(i + " exists " + counts[i] + " times.");
}
}
};
findTriplicates(array); // Salam exists 3 times.
please don't hesitate to fix my code or to post your solution.
thanks for your support in advance :)
Cheerz!
Your overall idea is good, using Hash Maps (js objects) is the best option for the task.
You can move your "=== 3" check to the first loop and have another object to save triplicates, it will be twice faster.
check this out
const findTriplicates = (param) => {
let values = [...new Set(param)];
let triples = [];
values.forEach(item=>{
let counter = 0;
param.forEach(s=>{
if(s===item) counter++;
})
if(3==counter) triples.push(item);
})
return triples;
};
There is no correct way to do things like this. You can always optimize or sacrifice performance for readability, but that is up to the developer.
I changed nothing about the functionality in findTriplicates, but the code is very different.
findTriplicates2 works a little different but is by no means superior
const array = [
"peace",
"peace",
"Vrede",
"Patz",
"Salam",
"paz",
"Salam",
"Salam"
];
const findTriplicates = (param) => {
let counts = param.reduce((acc, p) => {
acc[p] ? acc[p]++ : acc[p] = 1
return acc;
}, {})
Object.keys(counts).forEach((key) => counts[key] === 3 &&
console.log(`${key} exists 3 times.`)
);
};
findTriplicates(array); // Salam exists 3 times.
const findTriplicates2 = (param) => {
let triples = [...new Set(param)].reduce((acc, item) => {
let counter = param.reduce((acc2, s) => {
if (s === item) acc2++;
return acc2;
}, 0);
if (3 == counter) acc.push(item);
return acc;
}, [])
triples.forEach((triple) => console.log(`${triple} exists 3 times.`));
};
findTriplicates2(array); // Salam exists 3 times.
You create an object to keep count of how many times the string repeats and then iterate through each element in the array while updating the count in the object. Then you filter through that object for any values equal to 3.
const array = [
'peace',
'peace',
'Vrede',
'Patz',
'Salam',
'paz',
'Salam',
'Salam',
];
// Object to keep count of each word
const countObj = {};
// Iterate through the array to get the count for each word
array.forEach((element) => {
// Does word exist in the count object? if so -> add 1 to its count, else -> add word and count 1 to the object
countObj[element] ? (countObj[element] += 1) : (countObj[element] = 1);
});
// Filter out keys that appear exactly 3 times and print them them
const filteredArray = Object.keys(countObj).filter(
(key) => countObj[key] === 3
);
console.log(filteredArray); // Salam
Check if the second array contains the first array's element then show that otherwise show the second array element (which is not in the first one)
var contacts = [{name:'muzz',no:1},{name:'muzamil',no:2},{name:'hamza',no:3}]
var recipient = ['2','4']
function check () {
contacts.forEach(({name,no}) => {
if(recipient.includes(no.toString())){
console.log('exists',name)
}
else {
recipient.forEach(e =>{
if(!recipient.includes(no.toString()) && contacts == no){
console.log(e);
}
})
}
})
}
kindly tell me what I am missing here. The else block again traverses all the elements
Is this what you want?
function resolve (a, b) {
const map = a.reduce((acc, {no}, i) => (acc[no] = a[i], acc), {})
return b.reduce((acc, el) => (acc.push(map[el]?.name ?? el), acc), [])
}
var contacts = [{name:'muzz',no:1},{name:'muzamil',no:2},{name:'hamza',no:3}]
var recipients = ['2','4']
const resolved = resolve(contacts, recipients)
console.log(resolved)
You can loop through the recipient array first, then filter the object from the contacts array by matching the current recipient:
var contacts = [{name:'muzz',no:1},{name:'muzamil',no:2},{name:'hamza',no:3}]
var recipient = ['2','4']
function check () {
recipient.forEach(r => {
var c = contacts.filter(c => c.no == r);
if(c.length){
console.log('exists',c[0].name)
}
else {
console.log(r);
}
});
}
check();
I am building a react project for visualizing insertion sort using redux. I am using react-redux to create and handle actions. However, the problem is that in my insertionSort algorithm, I dispatch an updateArray action every time the array being sorted changes. I put print statements inside the reducer and saw that the state was in fact changing and the action was being dispatched correctly, however, my actual array does not re-render. I put prints inside the relevant UI component's render() function and saw that it was only being called once or twice rather than every time the reducer receives the action. I tried restructuring my code multiple times and reading about similar problems that people have had but their answers did not help me.
Am I just structuring this the wrong way? Should I not be using dispatches every second or so to update my array?
I have a main.js file which is used to render the UI components including my array:
class Main extends React.Component {
setArray = () => {
this.props.setArray(50, window.innerHeight / 1.4)
startSort = () => {
this.props.startSorting(this.props.algorithm, this.props.array)
}
render() {
let { array} = this.props
return (
<div>
<Navbar
startSort={this.startSort}
setArray={this.setArray}
/>
<MainWrapper>
<Row />
</MainWrapper>
</div>
)
}
}
const mapStateToProps = state => {
return {
array: state.array,
}
}
const mapDispatchToProps = dispatch => {
return {
setArray: (length, height) => {
let array = Array.from({ length: length }, () =>
Math.floor(Math.random() * height))
dispatch(setArray(array))
},
startSorting: (algorithm, array) => {
var doSort
if (algorithm == 'insertionSort') {
doSort = insertionSort
}
doSort(array, dispatch)
}
}
}
My actual array is generated with Row.js
class Row extends React.Component {
generateNodes(array) {
var elements = []
array.forEach((value, index) => {
elements.push(
<CenteredColumn>
<ArrayNode idx={index} value={value} />
</CenteredColumn>
)
})
return elements
}
render() {
let { array } = this.props
console.log('UPDATED ARRAY: ' + array)
var arrayElements = this.generateNodes(array)
return <RowWrapper>{arrayElements}</RowWrapper>
}
}
const mapStateToProps = state => {
return {
array: state.array
}
}
And finally, my actual algoritm is in insertionSort.js in which I import my actions from their reducers and pass in a dispatch function from main.js:
function delayedInsertion(array, dispatch) {
let n = array.length
var i = 0
function loop() {
setTimeout(function() {
var temp = array[i]
var j = i - 1
while (j >= 0 && array[j] > temp) {
array[j + 1] = array[j]
j--
}
array[j + 1] = temp
// console.log('ARRAY: ' + array)
dispatch(updateArray(array))
i++
if (i < n) {
loop()
}
}, 200)
}
loop()
console.log('DONE')
}
It seems that you are mutating your state.
You are passing this.props.array to your doSort action and as I understand your idea correctly, you are just calling delayedInsertion from that action (you did not post source code of that action).
But in delayedInsertion you are mutating the passed array when you are changing positions of you items, here:
while (j >= 0 && array[j] > temp) {
array[j + 1] = array[j]
j--
}
array[j + 1] = temp
You need to perform immutable change of the array.
I've been trying to debug weird issue and I've finally figured out why it's happening. Just not sure how to prevent it (; I have this function:
getInfo(id) {
id = id || "zero";
let i = routeDefinitions.findIndex(r => Boolean(r.name.toLowerCase().match(id)));
// console.log(i) - works in plunker
// but in my app sometimes returns -1...
let current = routeDefinitions[i];
let next = routeDefinitions[i + 1] ? routeDefinitions[i + 1] : false;
let prev = routeDefinitions[i - 1] ? routeDefinitions[i - 1] : false;
return { prev, current, next };
}
..it works perfectly in this plunker, but in my app I use its return value to update app state (custom implementation of redux pattern). When I send return value through this function:
private _update(_old, _new) {
let newState = Object.keys(_new)
.map(key => {
if (_old[key] === undefined) {
_old[key] = _new[key];
} else if (typeof _new[key] === "object") {
this._update(_old[key], _new[key]);
} else {
_old[key] = _new[key];
}
return _old;
})
.find(Boolean);
return Object.assign({}, newState || _old);
}
..routeDefinitions array is mutated and things start to break... I've tried couple of things:
let current = [...routeDefinitions][i];
// and:
return Object.assign({}, { prev, current, next });
..but it didn't work. How can I prevent mutatation of routeDefinitions array?
EDIT: I've managed to reproduce the error in this plunker
routeDefinitions array is mutated and things start to break
If your function is truly:
getInfo(id) {
id = id || "zero";
let i = routeDefinitions.findIndex(r => Boolean(r.name.toLowerCase().match(id)));
// console.log(i) - works in plunker
// but in my app sometimes returns -1...
let current = routeDefinitions[i];
let next = routeDefinitions[i + 1] ? routeDefinitions[i + 1] : false;
let prev = routeDefinitions[i - 1] ? routeDefinitions[i - 1] : false;
return { prev, current, next };
}
Then routeDefinitions is not mutated. Something else is mutating routeDefinitions.
I solved this by modifying _update() like this:
private _update2(_old, _new) {
let newState = {};
Object.keys(_new)
.map(key => {
if (_old[key] === undefined) {
newState[key] = _new[key];
} else if (typeof _new[key] === "object") {
newState[key] = this._update2(_old[key], _new[key]);
} else {
newState[key] = _new[key];
}
return newState;
})
.find(Boolean);
return Object.assign({}, _old, newState);
}
I use old state just to check values, don't modify it until later when _update() is finished.
Plunker