I'm doing this:
const rawValues = this.filterList.map(s => {
return {[s.filterLabel]: s.selectedOption}
});
filterList variable has this type:
export interface SelectFilter {
filterLabel: string;
options: Observable<any>;
selectedOption: string;
}
now rawValues is being mapped like this:
[
{filterLabel: selectedOption},
{filterLabel: selectedOption},
{filterLabel: selectedOption}
]
so it's an array of my new objects,
but what I want is a SINGLE object, so the end result should be:
{
filterLabel: selectedOption,
filterLabel: selectedOption,
filterLabel: selectedOption
}
NOTE that "filterLabel" will always be unique.
What do I need to change in the map() ?
For this use case, a map isn't needed as it would result in creating a new array which is unnecessary. Just iterate over each element in the array then assign each filterLabel as a new key to the obj like this:
const obj = {};
this.filterList.forEach(s => {
obj[s.filterLabel] = s.selectedOption;
});
console.log(obj);
I think this is use case for array reduce:
let result =
[{filterLabel: 'label1', selectedOption: 'option1'}, {filterLabel: 'label2', selectedOption: 'option2'}, {filterLabel: 'label3', selectedOption: 'option3'}, {filterLabel: 'label4', selectedOption: 'option4'} ]
.reduce(function(previousValue, currentValue, index, array) {
return {
[currentValue.filterLabel]: currentValue.selectedOption,
...previousValue }
}, {});
console.log(result);
More details:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
You shouldn't do anything to get the result you want. First, when you run a map on the array, a new array is returned. To change that you would have to re-write the map function with your own. Technically possible, but not recommended.
Second, you cannot have multiple properties on an object that have the exact same name. I know of no way around this.
You might be able to do something like what you want with a loop:
let rawValues = {};
for (i = 0; i < filterList.length; i++) {
rawValues[`${filterList[i].filterLabel}${i}`] = filterList[i].selectedOption;
}
Which should give you something like this:
{
filterLabel1: selectedOption,
filterLabel2: selectedOption,
filterLabel3: selectedOption
}
Can you guarantee that the filterLabel will always be unique?
var result = {};
this.filterList.forEach(s => {
result[s.filterLabel] = s.selectedOption;
});
you can use reduce to achieve the same result:
var result = this.filterList.reduce((prev, next) => {
return {...prev, [next.filterLabel]:next.selectedOption}
}, {});
Related
If you have an array as part of your state, and that array contains objects, whats an easy way to update the state with a change to one of those objects?
Example, modified from the tutorial on react:
var CommentBox = React.createClass({
getInitialState: function() {
return {data: [
{ id: 1, author: "john", text: "foo" },
{ id: 2, author: "bob", text: "bar" }
]};
},
handleCommentEdit: function(id, text) {
var existingComment = this.state.data.filter({ function(c) { c.id == id; }).first();
var updatedComments = ??; // not sure how to do this
this.setState({data: updatedComments});
}
}
I quite like doing this with Object.assign rather than the immutability helpers.
handleCommentEdit: function(id, text) {
this.setState({
data: this.state.data.map(el => (el.id === id ? Object.assign({}, el, { text }) : el))
});
}
I just think this is much more succinct than splice and doesn't require knowing an index or explicitly handling the not found case.
If you are feeling all ES2018, you can also do this with spread instead of Object.assign
this.setState({
data: this.state.data.map(el => (el.id === id ? {...el, text} : el))
});
While updating state the key part is to treat it as if it is immutable. Any solution would work fine if you can guarantee it.
Here is my solution using immutability-helper:
jsFiddle:
var update = require('immutability-helper');
handleCommentEdit: function(id, text) {
var data = this.state.data;
var commentIndex = data.findIndex(function(c) {
return c.id == id;
});
var updatedComment = update(data[commentIndex], {text: {$set: text}});
var newData = update(data, {
$splice: [[commentIndex, 1, updatedComment]]
});
this.setState({data: newData});
},
Following questions about state arrays may also help:
Correct modification of state arrays in ReactJS
what is the preferred way to mutate a React state?
I'm trying to explain better how to do this AND what's going on.
First, find the index of the element you're replacing in the state array.
Second, update the element at that index
Third, call setState with the new collection
import update from 'immutability-helper';
// this.state = { employees: [{id: 1, name: 'Obama'}, {id: 2, name: 'Trump'}] }
updateEmployee(employee) {
const index = this.state.employees.findIndex((emp) => emp.id === employee.id);
const updatedEmployees = update(this.state.employees, {$splice: [[index, 1, employee]]}); // array.splice(start, deleteCount, item1)
this.setState({employees: updatedEmployees});
}
Edit: there's a much better way to do this w/o a 3rd party library
const index = this.state.employees.findIndex(emp => emp.id === employee.id);
employees = [...this.state.employees]; // important to create a copy, otherwise you'll modify state outside of setState call
employees[index] = employee;
this.setState({employees});
You can do this with multiple way, I am going to show you that I mostly used. When I am working with arrays in react usually I pass a custom attribute with current index value, in the example below I have passed data-index attribute, data- is html 5 convention.
Ex:
//handleChange method.
handleChange(e){
const {name, value} = e,
index = e.target.getAttribute('data-index'), //custom attribute value
updatedObj = Object.assign({}, this.state.arr[i],{[name]: value});
//update state value.
this.setState({
arr: [
...this.state.arr.slice(0, index),
updatedObj,
...this.state.arr.slice(index + 1)
]
})
}
Despite the fact that the title seems difficult, let me give you a simple example.
I have an object.
{
name:"Ethan"
pets:{cat:"joline",dog:"Momo",bird"Mimi"}
}
My goal is to push the values of the array to the object.
{
name:"Ethan",
cat:"joline",
dog:"Momo",
bird"Mimi"
}
The question is simple and I believe you can approach it in a clever way, thanks.
Simple way to do this is to combine pets object and other properties using spread operator and then delete the pets from result.
const data = {
name:"Ethan",
pets:{cat:"joline",dog:"Momo",bird:"Mimi"}
}
const res = {...data, ...data.pets};
delete res.pets;
console.log(res);
If you want to do it in a functional way you can use the following approach.
Wrap the original element in a array.
Apply map on it.
Destructure pets from the object and store the rest of the properties in other variable.
Return a new object where you spread the rest object and pets.
const data = {
name:"Ethan",
pets:{cat:"joline",dog:"Momo",bird:"Mimi"}
}
const res = [data].map(({pets, ...rest}) => ({...rest, ...pets}))[0]
console.log(res)
The simplest and cleanest way to obtain desired result with destructuring.
const obj = {
name: "Ethan",
pets: { cat: "joline", dog: "Momo", bird: "Mimi" },
};
const { name, pets } = obj;
const result = { name, ...pets };
console.log(result);
Iterating through the elements and values in the object. And spreading the value into the return object
const data = {
name: "Ethan",
pets: {
cat: "joline",
dog: "Momo",
bird: "Mimi"
}
};
function flatten(data) {
let result = {};
for (const key in data) {
const value = data[key];
if (typeof value != 'object') result[key] = value;
else result = { ...result, ... flatten(value)}
}
return result;
}
console.log(flatten(data));
As others have said, this is straightforward with object destructuring. I think it's cleanest when you use parameter destructuring. So a simple function like this should do it:
const promotePets = ({pets, ...rest}) =>
({...rest, ...pets})
const obj = {name: "Ethan", pets: { cat: "joline", dog: "Momo", bird: "Mimi" }};
console .log (promotePets (obj))
before I use only nextJs everything is good to go but after I try to use recoil and I try to assign new value to array object by using .map() but the error show up
Cannot assign to read only property
Here is my example Array object
const [allData, setAllData] = useRecoilState(
allDataStatte
);
Here is example state AllData
const allData = [
{
id:1,
value:"test1"
},
{
id:2,
value:"test2"
}
]
Here is my code
const edit = (listId, value) => {
allData.map((data) => {
if (data.id === listId) {
data.value = value;
}
});
};
example I want to call edit funcion like this
edit(1,"newTitle1")
I want my new allData output look like this
const data = [
{
id:1,
value:"newTitle1"
},
{
id:2,
value:"test2"
}
]
I have read someone told that I have to use .slice() to create new object but still not use how to use slice with an array object
Here is what you need to do,
const [allData, setAllData] = useRecoilState(allDataState);
const edit = (listId : number, value : string) => {
let newAllData = allData.map((data) => {
let newData = {...data};
if (data.id === listId) {
newData.value = value;
}
return newData;
});
setAllData (newAllData);
};
edit(1, 'new value 1');
Noticed, newAllData is a new array. Also newData is a new object constructed from data.
it's because of atom in recoil you have to re create object array and then setState again by using _clondeep or slice
My array comes like this
var data=[{PRODUCT : P1}, {PRODUCT: P2}]
I wantt to convert this into [P1, P2].
Sometimes my array comes like this
var data=[{ITEM: I1, QUANTITY:1}, {ITEM: I2, QUANTITY:2}]
I wantt to convert this into [I1, I2].
so can we make a common function, where I just want to extract particular value of array and make a new array.
p.s. Thank you in advance
I tried to write the logic like this:
data.map((d, index) => { var result= [];
result.includes(d[0]); })
but it,s not dynamic
You could define a function which will always get the first value of the first object key, this should satisfy your needs based on the above
var data1 = [{
ITEM: 'I1',
QUANTITY: 1
}, {
ITEM: 'I2',
QUANTITY: 2
}]
var data2 = [{
PRODUCT: 'P1'
}, {
PRODUCT: ' P2'
}]
function getArrayOfValues(list) {
return list.reduce((acc, x) => {
const firstValue = Object.values(x)[0];
acc.push(firstValue)
return acc;
}, [])
}
const result1 = getArrayOfValues(data1)
console.log(result1)
const result2 = getArrayOfValues(data2)
console.log(result2)
function getProductOrItem(list) {
return list.reduce((accumulator, obj) => {
if (obj.PRODUCT) {
accumulator.push(obj.PRODUCT);
} else if (obj.ITEM) {
accumulator.push(obj.ITEM);
}
return accumulator;
}, [])
}
you can iterate through your array with map() method and inside it extract the value of a first entity of an object in your array and simply get a new array with all values:
const data1 =[{PRODUCT : 'P1'}, {PRODUCT: 'P2'}]
const data2 = [{ITEM: 'I1', QUANTITY: 1}, {ITEM: 'I2', QUANTITY: 2 }]
const transformValuesOfArray = (arrayToTransform) =>
arrayToTransform.map(value => {
const firstObjectValue = Object.values(value)[0]
return firstObjectValue
})
console.log(transformValuesOfArray(data1))
console.log(transformValuesOfArray(data2))
I have an array with below list of items as shown in image , I would like to remove the duplicates
[L7-LO, %L7-LO] from that array.
I have tried with the following conditions:
Scenario 1 :
this.formulalist.filter((el, i, a) => i == a.indexOf(el))
Scenario 2:
Observable.merge(this.formulalist).distinct((x) => x.Value)
.subscribe(y => {
this.formulalist.push(y)
});
Scenario 3:
this.formulalist.forEach((item, index) => {
if (index !== this.formulalist.findIndex(i => i.Value == item.Value))
{
this.formulalist.splice(index, 1);
}
});
None of the three scenarios above were able to remove the duplicates from that array. Could any one please help on this query?
angular is not necessary use vanillajs
filter the elements with only one occurrence and add to the new list the first occurrence
let newFormulalist = formulalist.filter((v,i) => formulalist.findIndex(item => item.value == v.value) === i);
Try populating a new array without duplicates. Assign the new array later to formulalist.
newArr = []
this.formulalist.forEach((item, index) => {
if (this.newArr.findIndex(i => i.Value == item.Value) === -1)
{
this.newArr.push(item)
}
});
this.formulalist = this.newArr
EDIT
Looking at the answer above, the solution seems so outdated. A better approach would have been to use an Array.filter() than a Array.forEach().
But, having a better solution would be nice, now when I see this question, I feel findIndex() not to be a good approach because of the extra traversal.
I may have a Set and store the values in the Set on which I want to filter, If the Set has those entries, I would skip those elements from the array.
Or a nicer approach is the one that is used by Akitha_MJ, very concise. One loop for the array length, an Object(Map) in the loop with keys being the value on which we want to remove duplicates and the values being the full Object(Array element) itself. On the repetition of the element in the loop, the element would be simply replaced in the Map. Later just take out the values from the Map.
const result = Array.from(this.item.reduce((m, t) => m.set(t.name, t), new Map()).values());
Hope this works !!
// user reduce method to remove duplicates from object array , very easily
this.formulalist= this.formulalist.reduce((a, b) => {
if (!a.find(data => data.name === b.name)) {
a.push(b);
}
return a;
}, []);
// o/p = in formulalist you will find only unique values
Use a reducer returning a new array of the unique objects:
const input = [{
value: 'L7-LO',
name: 'L7-LO'
},
{
value: '%L7-LO',
name: '%L7-LO'
},
{
value: 'L7-LO',
name: 'L7-LO'
},
{
value: '%L7-LO',
name: '%L7-LO'
},
{
value: 'L7-L3',
name: 'L7-L3'
},
{
value: '%L7-L3',
name: '%L7-L3'
},
{
value: 'LO-L3',
name: 'LO-L3'
},
{
value: '%LO-L3',
name: '%LO-L3'
}
];
console.log(input.reduce((acc, val) => {
if (!acc.find(el => el.value === val.value)) {
acc.push(val);
}
return acc;
}, []));
if you are working using ES6 and up, basic JS using map and filter functions makes it easy.
var array = [{value:"a"},{value:"b"},{value:"c"},{value:"a"},{value:"c"},{value:"d"}];
console.log(array.filter((obj, pos, arr) => {
return arr.map(mapObj => mapObj["value"]).indexOf(obj["value"]) === pos;
}));
Filtering for unique values is much faster with assigning values to some object properties - there not will be duplicates.
This approach gets better and better with every +1 member of initial array, because looping will be causing fast algorithm complications
let arr = [
{value: 'L7-LO', name: 'L7-LO'},
{value: '%L7-LO', name: '%L7-LO'},
{value: 'L7-LO', name: 'L7-LO'},
{value: '%L7-LO', name: '%L7-LO'},
{value: 'L7-L3', name: 'L7-L3'},
{value: '%L7-L3', name: '%L7-L3'},
{value: 'LO-L3', name: 'LO-L3'},
{value: '%LO-L3', name: '%LO-L3'}
];
let obj = {};
const unique = () => {
let result = [];
arr.forEach((item, i) => {
obj[item['value']] = i;
});
for (let key in obj) {
let index = obj[key];
result.push(arr[index])
}
return result;
}
arr = unique(); // for example;
console.log(arr);