Redux reducer failing to remove array element - javascript

I'm having problems trying to get my reducer to work correctly in Redux. I'm new to Redux so I might be missing something simple, but I've played with it for a while and can't figure out what's going wrong.
Here is my process:
Define argument:
First I define the index value that I need. When logged, this returns the correct number...
const thisCommentIndex = parseInt(comments.indexOf(comment))
Function Call:
<div onClick={this.props.removeComment.bind(null, thisCommentIndex)}></div>
Action:
export function removeComment(index) {
return {
type: 'REMOVE_COMMENT',
index
}
}
Reducer:
function comments(state = [], action) {
switch(action.type) {
case 'REMOVE_COMMENT' :
console.log('removing comment with index of ' + action.index)
return [
...state.slice(0, action.index), // why isn't this working???
...state.slice(action.index)
]
default :
return state
}
return state;
}
When I console.log('removing COMMENT with index of ' + action.index), it logs the action.index correctly, the integer I would expect. But the function doesn't remove the element as expected.
Strangely, if I simply pass the array index instead, it works fine (removes the array element). (I would just do this, but due to the way I have set up my app it won't work in this case).
Am I missing something here? Any help appreciated.

You're missing a +1...
return [
...state.slice(0, action.index),
...state.slice(action.index + 1) // <--- need to actually skip what you want to remove
]

#Jack is correct. Another option would be to use Array.filter instead:
return state.filter( (item, index) => index !== action.index)
You might be interested in the new Structuring Reducers section of the Redux docs. In particular, the page on Immutable Update Patterns has some related examples.

If you want to remove multiple items, then you could work through your array backwards
for (var i = this.props.items.length -1; i >= 0; --i) {
if(this.props.items[i]["selected"]) {
this.props.deleteSelectedItem(i);
}
}

Related

Javascript Equalize Element From Array

Guys I want to get an element from array. Here:
Follower:
{ follower:
[ 5edfe8f3bfc9d677005d55ca,
5edfe92fbfc9d677005d55cc,
5ee2326cc7351c5bb0b75f1a ],
user id:
5edfe92fbfc9d677005d55cc
The process:
if(follower == user){
console.log("sdasdsad")
}else{
console.log("no")
}
But when I do it it always returns as no.
Also this is the codes of===> Nodejs Follow System Not Working Properly
It is a nodejs project. So please look at the above link.
When I do
if(follower.includes(user)){
It gives the error of:
TypeError: Cannot read property 'includes' of null
And when I try to change some I get this error:
TypeError: takip.includes is not a function
Guys so thats why I say please look at the above code.
So how to equalize them?
As other peoples said earlier the follower itself is a property which its value is an array itself, so if you want to check whether an item exists within it or not you can check it with includes(), if it exists it will return true otherwise it will return false.
const follow = {
follower: ["5edfe8f3bfc9d677005d55ca",
"5edfe92fbfc9d677005d55cc",
"5ee2326cc7351c5bb0b75f1a"
]
}
const user = "5edfe92fbfc9d677005d55cc";
if (follow.follower.includes(user)) {
console.log("sdasdsad")
} else {
console.log("no")
}
But if you looking to find the exact position of the item within that array you can find it with indexOf(). If the item does not exist within the array it will return -1, otherwise, it will return the index of that item within the array.
const follow = {
follower: ["5edfe8f3bfc9d677005d55ca",
"5edfe92fbfc9d677005d55cc",
"5ee2326cc7351c5bb0b75f1a"
]
}
const user = "5edfe92fbfc9d677005d55cc";
console.log(follow.follower.indexOf(user));
You are trying to compare a string to an array so it will never pass the if statement.
If you change your if to be if ( follower.includes(user)) { then it will search the array for the string.
var follower = [
'5edfe8f3bfc9d677005d55ca',
'5edfe92fbfc9d677005d55cc',
'5ee2326cc7351c5bb0b75f1a'
]
var user = '5edfe92fbfc9d677005d55cc'
// This will always fail as follower is an array not a string
if (follower.includes(user)){
console.log("sdasdsad")
} else {
console.log("no")
}
References
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes
Looks like follower is a property. You can use this solution:
objectName.follower.forEach(item =>
if (item == user) console.log(`${item} is the answer`);
);
This way, javascript will go through all of the elements in the array and print it out if it is matching with your user variable.
You can also use for loop or while loop for the same process, however, since you're using an array, forEach will be much more useful.
If this was not your question and I misunderstood your question, let me know, I'll see if I can help.
I hope this helps
var obj = {
follower: [ '5edfe8f3bfc9d677005d55ca',
'5edfe92fbfc9d677005d55cc',
'5ee2326cc7351c5bb0b75f1a'
]
};
var userId = '5edfe92fbfc9d677005d55cc';
function searchUser(object, user){
if(obj.follower.includes(user)){
return object.follower.filter(x => x == user);
} else {
return 'no';
}
};
console.log(searchUser(obj, userId));
You can use Array.protorype.some() to check if user exists in the follower array.
const obj = {
follower: [
"5edfe8f3bfc9d677005d55ca",
"5edfe92fbfc9d677005d55cc",
"5ee2326cc7351c5bb0b75f1a"
]
}
const user = "5edfe92fbfc9d677005d55cc";
if(obj.follower.some(item => item === user)) {
console.log("found")
} else{
console.log("no")
}
You can also get the item with Array.protorype.find() with the same way as above, just assign it to a variable
Array.prototype.some
Array.prototype.find

How can I map a concatenated value for this state, without it being passed as a string?

I'm new to Javascript.
I'm building this drumpad and I want to be able to switch between different soundpacks.
The sounds are imported from a separate file and the state is written like this:
import * as Sample from '../audiofiles/soundfiles'
const drumpadData = [
{
// other stuff
soundfile: Sample.sound1a
},
If I want to load a different soundpack, I have to change the state so the last letter (a,b,c) gets changed, so instead of Sample.sound1a, it would have to be Sample.sound1b. this is the function i wrote (on App.js):
changeSamples(id) {
let choice = document.getElementById("select-samplepack").value
this.setState(prevState => {
const updatedData = prevState.data.map(item => {
let newSoundfile = "Sample.sound" + item.id + choice
item.soundfile = newSoundfile
return item
})
return {
data: updatedData
}
})
}
It works, as in the value gets changed, but instead of react interpreting that and finding the correct import, the value of soundfile just stays as a string like "Sample.soundb1", so I get a load of media resource errors.
https://aquiab.github.io/drumpad/ heres the website, you can check the console to see the error, you have to load a different soundpack to reproduce the error.
https://github.com/aquiab/drumpad and here are the files:
I've thought of some ways of cheesing it, but I want the code to stay as clean as I can make it be.
Well that's because it is in fact a string. When you do:
"Sample.Sound" + item.id + choice you are doing type coersion. In JavaScript, that means you are converting the value of all data-types so that they share a common one. In this case your output resolves into a string. This will not be effective in finding the right sound in your dictionary.
Instead, what you need is bracket notation: Object[property]
Within the brackets we can define logic to identify the designated key belonging to the Object.
For example: Sample["sound" + item.id + choice] would evaluate to Sample["sound1b"] which is the same as Sample.sound1b
changeSamples(id) {
let choice = document.getElementById("select-samplepack").value
this.setState(prevState => {
const updatedData = prevState.data.map(item => {
item.soundfile = Sample["sound" + item.id + choice]
return item
})
return {
data: updatedData
}
})
}
I can think of two approaches here.
import Sample in App.jsx. and update sound file.
import * as Sample from '../audiofiles/soundfiles'
changeSamples(id) {
let choice = document.getElementById("select-samplepack").value
this.setState(prevState => {
const updatedData = prevState.data.map(item => {
let newSoundfile = Sample[`sound${item.id}${choice}`]
item.soundfile = newSoundfile
return item
})
return {
data: updatedData
}
})
}
You should save mapping of files in other object and update mapping and use mapping in drumpadData soundfile key.
Your problem comes from this line :
let newSoundfile = "Sample.sound" + item.id + choice
Here you are concatening your values item.id and choice with a string, so the result is a string and Sample is not interpreted as your imported object.
What you need is to wright something like
const sampleItem = "sound" + item.id + choice
let newSoundfile = Sample[sampleItem]
When you access an object property with the notation myObject[something], what's inside the bracket get interpreted. So in my example sample (which is a string because I concatenated a string "sound" with the variables) will be replaced with its string value (ex: "sound1a"), and newSoundFile will have as value the result of Sample["sound1a"].
I hope it make sens.

Iterate over an array of objects with object having multiple properties

I'm trying to iterate over the following:
state = {
products : [
{
x : "sd",
y : "fdg"
}, {
x : "sdx",
y : "fdgx"
}
]
}
I need to iterate over the above products array and inside object to create:
<tr><td>sd</td><td>fdg</td></tr>
I tried using :
{
this.state.products.map(function(prod) {
return <tr><td>{prod.x}</td><td>{prod.y}</td></tr>;
})
}
but get multiple errors and prod being undefined.
It's possible that logic elsewhere in your component is mutating the state, which in turn may be the root cause of the error thrown during rendering.
Be sure to check that the products array is consistently defined in your components state, and that the items in that array are defined.
One solution here might be to take a more defensive approach to rendering your table row elements, by doing the following:
{
Array.isArray(this.state.products) && this.state.products
.filter(product => !!product)
.map(product => {
return <tr><td>{product.x}</td><td>{product.y}</td></tr>;
})
}
Here, the rendering logic asserts that this.state.products is the expected array type via Array.isArray(). Addtionally, the logic ensures any prop being rendered is defined by first filtering out any undefined prop items via this line:
filter(product => !!product)
Hope that helps!
The problem is that the return statement is an HTML code which is causing the problem whereas you can encode the code into a string and the DOM will treat it as HTML code
this.state.products.map(function(prod){ return "<tr><td>"+prod.x+"</td><td>"+prod.y +"</td> </tr>" }).
you need to add that in one variable return as below:
const prod = this.state.products.map(function(prod) {
return <tr><td>{prod.x}</td><td>{prod.y}</td></tr>;
});
Use the variable inside render lifecycle as below.
{prod}
Here is the working code attached in jsFiddle
Hope this helps!

Comparing 2 Json Object using javascript or underscore

PS: I have already searched the forums and have seen the relevant posts for this wherein the same post exists but I am not able to resolve my issue with those solutions.
I have 2 json objects
var json1 = [{uid:"111", addrs:"abc", tab:"tab1"},{uid:"222", addrs:"def", tab:"tab2"}];
var json2 = [{id:"tab1"},{id:"new"}];
I want to compare both these and check if the id element in json2 is present in json1 by comparing to its tab key. If not then set some boolean to false. ie by comparing id:"tab1" in json2 to tab:"tab1 in json1 .
I tried using below solutions as suggested by various posts:
var o1 = json1;
var o2 = json2;
var set= false;
for (var p in o1) {
if (o1.hasOwnProperty(p)) {
if (o1[p].tab!== o2[p].id) {
set= true;
}
}
}
for (var p in o2) {
if (o2.hasOwnProperty(p)) {
if (o1[p].tab!== o2[p].id) {
set= true;
}
}
}
Also tried with underscore as:
_.each(json1, function(one) {
_.each(json2, function(two) {
if (one.tab!== two.id) {
set= true;
}
});
});
Both of them fail for some test case or other.
Can anyone tell any other better method or outline the issues above.
Don't call them JSON because they are JavaScript arrays. Read What is JSON.
To solve the problem, you may loop over second array and then in the iteration check if none of the objects in the first array matched the criteria. If so, set the result to true.
const obj1 = [{uid:"111", addrs:"abc", tab:"tab1"},{uid:"222",addrs:"def", tab:"tab2"}];
const obj2 = [{id:"tab1"},{id:"new"}];
let result = false;
for (let {id} of obj2) {
if (!obj1.some(i => i.tab === id)) {
result = true;
break;
}
}
console.log(result);
Unfortunately, searching the forums and reading the relevant posts is not going to replace THINKING. Step away from your computer, and write down, on a piece of paper, exactly what the problem is and how you plan to solve it. For example:
Calculate for each object in an array whether some object in another array has a tab property whose value is the same as the first object's id property.
There are many ways to do this. The first way involves using array functions like map (corresponding to the "calculate for each" in the question, and some (corresponding to the "some" in the question). To make it easier, and try to avoid confusing ourselves, we'll do it step by step.
function calculateMatch(obj2) {
return obj2.map(doesSomeElementInObj1Match);
}
That's it. Your program is finished. You don't even need to test it, because it's obviously right.
But wait. How are you supposed to know about these array functions like map and some? By reading the documentation. No one help you with that. You have to do it yourself. You have to do it in advance as part of your learning process. You can't do it at the moment you need it, because you won't know what you don't know!
If it's easier for you to understand, and you're just getting started with functions, you may want to write this as
obj2.map(obj1Element => doesSomeElementInObj1Match(obj1Element))
or, if you're still not up to speed on arrow functions, then
obj2.map(function(obj1Element) { return doesSomeElementInObj1Match(obj1Element); })
The only thing left to do is to write doesSomeElementInObj2Match. For testing purposes, we can make one that always returns true:
function doesSomeElementInObj2Match() { return true; }
But eventually we will have to write it. Remember the part of our English description of the problem that's relevant here:
some object in another array has a tab property whose value is the same as the first object's id property.
When working with JS arrays, for "some" we have the some function. So, following the same top-down approach, we are going to write (assuming we know what the ID is):
In the same way as above, we can write this as
function doesSomeElementInObj2Match(id) {
obj2.some(obj2Element => tabFieldMatches(obj2Element, id))
}
or
obj2.some(function(obj2Element) { return tabFieldMatches(obj2Element, id); })
Here, tabFieldMatches is nothing more than checking to make sure obj2Element.tab and id are identical.
We're almost done! but we still have to write hasMatchingTabField. That's quite easy, it turns out:
function hasMatchingTabField(e2, id) { return e2.tab === id; }
In the following, to save space, we will write e1 for obj1Element and e2 for obj2Element, and stick with the arrow functions. This completes our first solution. We have
const tabFieldMatches = (tab, id) { return tab === id; }
const hasMatchingTabField = (obj, id) => obj.some(e => tabFieldMatches(e.tab, id);
const findMatches = obj => obj.some(e => hasMatchingTabField(e1, obj.id));
And we call this using findMatches(obj1).
Old-fashioned array
But perhaps all these maps and somes are a little too much for you at this point. What ever happened to good old-fashioned for-loops? Yes, we can write things this way, and some people might prefer that alternative.
top: for (e1 of obj1) {
for (e2 of (obj2) {
if (e1.id === e2.tab) {
console.log("found match");
break top;
}
}
console.log("didn't find match);
}
But some people are sure to complain about the non-standard use of break here. Or, we might want to end up with an array of boolean parallel to the input array. In that case, we have to be careful about remembering what matched, at what level.
const matched = [];
for (e1 of obj1) {
let match = false;
for (e2 of obj2) {
if (e1.id === e2.tab) match = true;
}
matched.push(match);
}
We can clean this up and optimize it bit, but that's the basic idea. Notice that we have to reset match each time through the loop over the first object.

How to update or add to immutable.js List

I'm using Redux and Immutable.js in my React-based project (built on React Boilerplate) and I'm looking for an idiomatic way to update or add to an Immutable.js List.
My current setup. State initially looks like this:
const initialState = fromJS({
accounts: [],
activeAccount: null,
loadedAccounts: [],
});
I have an Immutable Record for an account object:
const account = new Record({
description: '',
id: '',
name: '',
status: '',
});
And my reducer:
function reducer(state = initialState, action) {
switch (action.type) {
case LOAD_ACCOUNT_SUCCESS:
return state
.set('activeAccount', new account(action.account))
default:
return state;
}
}
This works fine - when a LOAD_ACCOUNT_SUCCESS action is fired, activeAccount is updated to the value of action.account.
I can amend this so that every new LOAD_ACCOUNT_SUCCESS action pushes the newly-loaded account data to loadedAccounts instead:
function reducer(state = initialState, action) {
switch (action.type) {
case LOAD_ACCOUNT_SUCCESS:
const loadedAccount = new account(action.account);
return state
.update('loadedAccounts', (loadedAccounts) => loadedAccounts.push(loadedAccount));
default:
return state;
}
}
However, at the moment loading the same account data twice will result in new Records being pushed to my List each time (duplicating data). What I want to do instead is either add action.account to loadedAccounts (as happens now) or update the Record in the List if there is a matching ID. I'm looking at similar questions and the lamentable Immutable.js documentation and I can't see how to do this: no syntax I've tried works as I expect here.
So, what do you need here is nested update.
At first, you have to check your list of loadedAccounts whether it has this account or not.
Secondly, you have to change activeAccount field.
And, lastly, add (or update) account to loadedAccounts.
The caveat here is how you pass account property. If you derive it from somewhere and pass around as a Record, you can just compare by === (or by .equals()), but it seems that it is just a plain javascript object – I'll suppose it later.
In terms of code it would be something like:
// we can do it by different ways, it is just one of them
const listWithLoadedAccounts = state.get('loadedAccounts');
const isAccountAlready = Boolean(
listWithLoadedAccounts.filter(
account => account.get('id') === action.account.id
).size
);
const patchedState = state.set('activeAccount', action.account.id);
return isAccountAlready
? patchedState.updateIn(['loadedAccounts'], list => list.map(account => account.get('id') === account.action.id ? new account(action.account) : account))
: patchedState.updateIn(['loadedAccounts'], list => list.concat(new account(action.account)))
It is not the ideal code, something can be deduplicated, but you get the idea – always use deep merge / update if you need to change nested fields or data structures.
You also can set new field directly, like:
const oldList = state.get('loadedAccounts');
const newList = oldList.concat(action.account);
const patchedState = state.set('loadedAccounts', newList);
But I personally find that it is not that flexible and also not consistent, because it is quite common operation to perform deep merge.
i hope this example will help, i am creating a new immutable list and first performing an update and then adding a new element. i am not passing the object which i want to replace with, but you can also pass your existing object, Also in update method you have access to current item
class Test {
a = null;
b = null;
constructor(a,b){
this.a=a;
this.b=b;
}
}
$("#test").html("");
function logme(item){
$("#test").append("<br/>"+JSON.stringify(item));
}
function logmeNewLine(){
$("#test").append("<br/>");
}
function listAddUpadte(key){
var index= list.get('data').findIndex(listing => {
return listing.a === key;
});
logme(' found index (-1 for not found) : ' + index);
if(index >= 0){
logme("upadte");
list = list.set("data",list.get("data").update(index,function(item){
return new Test(key,"go");
}));
}else {
logme("add");
list = list.set("data",list.get("data").push(new Test(key,"go")));
}
list.get('data').forEach(item=>{
logme(item);
});
}
var list = Immutable.fromJS({
"data":[new Test(6,"abhi"),new Test(4,"raj"),new Test(1,"ajay")]
});
logme("intial data");
list.get('data').forEach(item=>{
logme(item);
});
logmeNewLine();
logme("testing replace with a = 4 ")
logmeNewLine();
listAddUpadte(4);
logmeNewLine();
logme("testing add with a = 8 ")
logmeNewLine();
listAddUpadte(8);
logmeNewLine();
logmeNewLine();
logmeNewLine();
<script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/3.7.2/immutable.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="test"></div>
Since this is the top search result for what is in the title of the question
https://immutable-js.github.io/immutable-js/docs/#/List/push
An example of how to add to a list using immutable.js:
let oldList = List([ 1, 2, 3, 4 ])
let newList = oldList.push(5)
https://immutable-js.com/docs/v4.0.0/List/#insert()
insert()
Returns a new List with value at index with a size 1 more than this List. Values at indices above index are shifted over by 1.
insert(index: number, value: T): List
Discussion
This is synonymous with list.splice(index, 0, value).
List([ 0, 1, 2, 3, 4 ]).insert(6, 5)
// List [ 0, 1, 2, 3, 4, 5 ]

Categories

Resources