I have this constant packages that includes an array of object_tags
const packages = [{
"id":"11",
"object_tags":[
{
"id":"400",
"tag":{
"id":"30",
"name":"Sample Tag"
},
}
],
},
{
"id":"12",
"object_tags":[
{
"id":"401",
"tag":{
"id":"31",
"name":"Lost"
},
}
],
}]
and I want to filter the packages that only have the tag with the name Lost. So I tried .filter()
this.taggedOrders = packages.filter(item => item.object_tags[0].tag.name === 'Lost');
But I am getting an error:
Uncaught TypeError: Cannot read property 'tag' of undefined
So I tried doing nested .filter()
this.taggedOrders = packages.filter(item => item.object_tags.filter(x => x.tag.name === 'Lost'));
but now it just returns the whole array, nothing filtered.
What am I missing?
I got it. I used .some()
this.taggedOrders = packages.filter(item => item.object_tags.some(x => x.tag.name === 'Lost'));
you are almost there, with nested filter, you need to get to the first filter condition as well.. in this case you can use condition where the length > 0. ex:
this.taggedOrders = packages.filter(p => p.object_tags.filter(t => t.tag.name === 'Lost').length > 0);
Related
I have an array which contains following objects.
myArray = [
{ item: { id: 111557 } },
{ item2: { id: 500600 } }]
and I have a variable
targetItemID = '111557'
Note that one is string, and the ones in array are numbers. I'm trying to get the object having the correct item id.
Here is what I have tried,
myArray = [
{ item: { id: 111557 } },
{ item2: { id: 500600 } }]
targetItemID = '111557'
var newArray = myArray.filter(x => {
console.log(x.item.id.toString())
console.log(targetItemID.toString())
x.item.id.toString() === itemID.toString()
})
console.log(newArray);
I expect all matching objects to be added to 'newArray'. I tried to check the values before comparison, They are both strings, they seem exactly same, but my newArray is still empty.
Your second object doesn't have an item property and should.
You need a return in your filter function.
You must compare x.item.id against targetItemID, not itemID. Since you are using console.log() you would have seen and error of itemID id not defined ;).
myArray = [
{ item: { id: 111557 } },
{ item: { id: 500600 } }
];
targetItemID = '111557'
var newArray = myArray.filter(x => {
//console.log(x.item.id.toString())
//console.log(targetItemID.toString())
return x.item.id.toString() === targetItemID.toString();
});
console.log(newArray);
There are a few issues here. First, not all your objects have an item property, so you'll need to check it exists. Second, you're comparing them against a non-existent itemID instead of targetItemID, and finally, and #bryan60 mentioned, if you open a block in an anonymous lambda, you need an explicit return statement, although, to be honest, you really don't need the block in this case:
var newArray =
myArray.filter(x => x.item && x.item.id && x.item.id.toString() === targetItemID)
you need to return for filter to work:
return x.item.id.toString() === itemID.toString();
So I have an array of names:
const names = ['student1', 'student2', 'student3']
and I have an array of attendance objects:
const attendance = [
{student1: ['On Time', 'Late']},
{student2: ['Late', 'Late']},
{student3: ['On Time', 'Excused']},
]
I wanted to find the student object in the attendance array based off the names from the names array.
So currently I have:
names.forEach(person => {
function attendanceData(p) {
return Object.keys(attendance).toString() == p.toString()
}
console.log(attendance.find(attendanceData(person)))
})
However, this gives me an error saying:
Uncaught (in promise) TypeError: false is not a function
The next stack says "at Array.find()"
I'm wondering how I'm not using this correctly and if there was a better way to do this, what should I do?
I believe this is what you want. Your data is structured a little strangely though, so I would like to know on a grander scale what you want this code to do.
const findStudentAttendance = (att, studentName) => {
return att.find(obj => Object.keys(obj)[0] === studentName)
}
names.forEach(name => {
console.log(
findStudentAttendance(attendance, name)
)
}) /* =>
{ student1: [ 'On Time', 'Late' ] }
{ student2: [ 'Late', 'Late' ] }
{ student3: [ 'On Time', 'Excused' ] }
*/
try it
let result = attendance.filter(obj => names.includes(Object.keys(obj)[0]))
console.log(result)
Use array's method "filter" for getting items. You need get keys of object and take first key. And then you'll can check presence in names array, via array's method "includes"
more information about this methods here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Filter
I have a computed property:
relateditems () {
return this.related.find((relation) => {
return relation.id === this.currentItem.id
})
},
with the following output:
relateditems:Object
-KQ1hiTWoqAU77hiKcBZ:true
-KQ1tTqLrtUvGnBTsL-M:true
id:"-KQ1if2Zv3R9FdkJz_1_"
I'm trying to create another computed property that then loops through the relateditems object and finds a relation with the matching ID for the first two keys.
The following doesn't work but I think it gives the idea:
relateditemsdata () {
let test = []
for (var key in this.relateditems) {
this.items.find((relateditem) => {
relateditem.id === key.id
test.push(relateditem)
})
}
return test
}
I think calling a computed property in another one is not a good way, so you could add a watcher property in order to watch the first computed property and update a data object property based on that one like :
data() {
return {
relateditemsdata: [],
}
},
computed: {
relateditems() {
return this.related.find(relation => {
return relation.id === this.currentItem.id
})
},
},
watch: {
relateditems(val) {
for (var key in this.relateditems) {
this.items.find(relateditem => {
relateditem.id === key.id
relateditemsdata.push(relateditem)
})
}
},
},
Seems like your problem is not related to Vue.
Your key.id is undefined in the for..in loop. You would have to use this.relateditems[key] to access the value or just key to access the key. Since you do not want filter your other array for the 'id' key, you should also filter your object-keys first.
E.g.
relatedItems() {
return this.related.find((item) => {
return this.item.id == this.currentItem.id;
});
} // returns first objects with the same id
relatedItemsData() {
// grabs all keys except 'id'
const keys = Object.keys(this.relateditems).filter((key) => key != 'id');
this.items.filter((item) => {
return keys.indexOf(item.id) != -1; // checks if item.id is inside the keys-array
});
} // returns again array of objects;
Instead of a nested loop, you can also just use the Array.prototype.filter() function, like above.
I am trying to learn the map method. If I use this syntax response.data.map(d => I am able to iterate data array and see the results, but if I use this syntax response.data.map(([label, CustomStep]) => {, I am getting the error below:
Unhandled Rejection (TypeError): Invalid attempt to destructure non-iterable instance
Can you tell me how to fix it, so that in future I will fix it myself?
Providing my code snippet below:
axios
.get('http://world/sports/values')
.then(response => {
console.log("sports--->", response.data.map(d => d.customFieldValueName));
//this.setState({ playerRanks: response.data.map(d => d.customFieldValueName) });
// es6 map
//Unhandled Rejection (TypeError): Invalid attempt to destructure non-iterable instance
this.setState({
playerRanks: response.data.map(([label, CustomStep]) => {
label.customFieldValueName
})
})
})
update 1:
hey, I saw in console, data is an array inside that there are so many objects
data: Array(19)
[
{
"customFieldValueCode": "player1",
"customFieldValueName": "player1",
"isActive": "Y"
},
{
"customFieldValueCode": "player 2",
"customFieldValueName": "player 2",
"isActive": "Y"
}
]
EDIT:
Based off the data structure provided you could modify your code to...
axios
.get('http://world/sports/values')
.then(response => {
this.setState({
playerRanks: response.data.map(obj => {
return obj.customFieldValueName
})
})
})
OR
...
response.data.map(({customFieldValueName}) => {
return customFieldValueName;
})
...
OR even...
...
response.data.map(({customFieldValueName}) => customFieldValueName)
...
But this would be my recommended solution to provide type checking on you data and proper error handling...
axios
.get('http://world/sports/values')
.catch(err=> console.log(err))
.then(({data}) => { // Axios always returns an Object, so I can safely 'attempt' to destructure 'data' property
if (data && data.length) { // making sure 'data' does exist, it is an Array and has > 0 elements
this.setState({
playerRanks: data.map(obj => { // Not destructuring here in case obj isn't actually an Object
if (obj && obj.customFieldValueName) return customFieldValueName;
return null;
}).filter(elem=> elem) // BIG-O notation: This sequence is O(2N), as in iterates over the entire Array first with .map(), then iterates over the entire Array again with .filter() to clear out 'null' values
})
}
})
In order to prevent your returned Array above from having a bunch of null elements when they don't conform to our assertions, you can use an Array.reduce() method to 'filter' out any nulls...
axios
.get('http://world/sports/values')
.catch(err=> console.log(err))
.then(({data}) => { // Axios always returns an Object, so I can safely 'attempt' to destructure 'data' property
if (data && data.length) { // making sure 'data' does exist, it is an Array and has > 0 elements
this.setState({
playerRanks: data.reduce((acc,obj) => { // Not destructuring here in case obj isn't actually an Object
if (!obj || !obj.customFieldValueName) return acc; // If it doesn't meet assertions just return the existing accumulator (don't add another element .ie 'null')
return [
...acc, // If it conforms to the assertions the return a new accumulator, by first spreading in all existing elements and the adding the new one (customFieldValueName)
customFieldValueName
]
},[]) // BIG-O notation: This is O(1N) or O(N), as in it will only iterate over the Array one time and the reduce() function will filter out 'null' values at the same time
})
}
})
NOTE:
I also just added .filter(elem=> elem) to the end of my first example, which does the same thing as the new .reduce() functionality, but does this in 1N not 2N operations.
PRE-logged data
Here's how the Array.map() method works...
[1,2].map(element=> {
// element === 1, first iteration,
// element === 2, second iteration
})
Here's how Array destructuring works...
[one, two, ...theRest] = [1,2,3,4,5]
// one === 1 and two === 2 and theRest = [3,4,5]
Here's how Object destructuring works...
{one, three, ...theRest} = {one: 1, two: 2, three: 3, four: 4, five: 5}
// one === 1 and three === 3 and theRest === {two: 2, four: 4, five: 5}
// notice order doesn't matter here (three vs two), but you need to access valid properties from the object you're deetructuring from
So based on the way you function is structured you are making the assumption that the data structure of response.data is...
response.data === [
[
{ customFieldValueName: 'any value' }, // label
{} // CustomStep (this could be any value, not necessarily an Object)
],
[
{ customFieldValueName: 'any value' }, // label
'any value' // CustomStep
]
]
I hope this helps conceptually, but if you'd like a workable solution we will need...
Data structure of response.data. Can you provide result of console.log( JSON.stringify( response.data, null, 5) )
Specific values you are trying to assign to the new this.state.playerRanks Array.
PS: A good way to see Object destructuring in action with your current code is to change...
.then( response => {
To
.then( ({data}) => {
In this case, you should be certain that response.data is an array of arrays, because for each iteration of response.data.map, the function you are providing to the map must receive an array to be able to successfully pull the label and CustomStep values, due to the syntax with which you are destructuring the function parameter.
Imagine data in the following example is the response.data and the parseData function is the function you are passing to the map:
let data = [
[{ customFieldValueName: 'field name' }, { stepData: {} }],
[{ customFieldValueName: 'another field name' }, { stepData: {} }]
];
let parseData = ([label, CustomStep]) => console.log(label.customFieldValueName);
parseData(data[0]); // prints out 'field name'
Otherwise, if response.data is an array of objects, which it seems like it is due to you successfully being able to run response.data.map(d => d.customFieldValueName), you could update your map to this (if you simply want to pull the customFieldValueName value out of the object):
response.data.map(({ customFieldValueName }) => customFieldValueName)
I cannot figure out what I missed on line row.sections[SECTION_ID. It always show me a typo error ','...
FAQ: sections - is an array with objects inside. In this case I'm
trying modify the specific object of the sections founded by custom
flag SECTION_ID.
P.S.
I also tried to put row.sections[SECTION_ID] inside an extra brackets [], but unfortunately it does not help... Any solutions?
rows: state.rows.map(
row =>
row.ID === action.rowID
? {
...row,
sections: [
...row.sections,
row.sections[SECTION_ID]: { // error is here
...row.sections[SECTION_ID],
data: {
...// some data
}
}
]
}
: row
)
You cannot mutate some element inside array by spread operation in such way. Using this approach you'll jus add a new, mutated element to the same array each time. So, it you want to make it right, you need to use the map iterator instead:
rows: state.mymaps.rows.map(
row =>
row.ID === action.rowID
? {
...row,
sections: row.sections.map(
(section, index) =>
index === JOIN_SECTION_ID
? {
...section,
data: {
...section.data
}
} : section
)
} : row
)
If you're trying to replace an element at a certain index of the array without mutating the array, you can make a shallow copy of the array, and then set the value at that index. For example:
state.rows.map((row) => {
if (rowID !== action.rowID) {
return row;
}
const sectionCopy = [...row.sections];
sectionCopy[SECTION_ID] = {
...row.sections[SECTION_ID],
data: {
// some data
},
};
return {
...row,
sections: sectionCopy,
};
});