Checking and displaying values of array compared to another array in React - javascript

One data set is an object of arrays of ids and another is an object of arrays of ids and names. What I'd like to do is check if the ids from the first data exist in the second data set and if they do then display the names.
This is what is being called by the component, which works correctly:
<td>Genre</td>
<td>{this.matchGenres(this.props.movie.genre_ids, this.props.genres)}</td>
And this is the function that I can't get to work:
matchGenres = (genres, genreList) => {
genres.forEach((genre) => {
genreList.filter((list) => {
return list.id === genre;
}).map((newList) => {
return newList.name;
});
});
}
It looks like the operation performs correctly and returns the right names when I console.log it! But! its not showing up in the component on render.

const genres = [{
id: 1,
name: "Jazz Music"
}, {
id: 2,
name: "Something"
}];
const genreList = [1, 10, 100];
matchGenres = (genres, genreList) => genres
.filter(genre => genreList.includes(genre.id))
.map(genre => genre.name);
const matchedGenres = matchGenres(genres, genreList);
console.log(matchedGenres);

But! its not showing up in the component on render.
Its because your function doesn't return anything. You return inside filter and map and your function does not return anything. Also note that forEach always return undefined
You just need a minor change. Try this
let genres = ["1", "2", "3"];
let genreList = [{
id: "2",
name: "Two"
}, {
id: "32",
name: "Three"
}]
matchGenres = (genres, genreList) => {
return genreList.filter((list) => {
// findIndex return array index if found else return -1 if not found
return genres.findIndex(genere => genere === list.id) > -1;
}).map(list => list.name);
}
console.log(matchGenres(genres, genreList));

This is the solution that ended up working:
if (genreList.length !== 0) {
return genres.map(genre => genreList.find(list => list.id === genre)).map((newList) => newList.name) + ',';
}
For some reason the value of GenreList, which is an array, was showing up as empty for the first couple times the function is call. Thats another problem I'll have to look at but the if statement solves for it for the time being.

Related

subjects.map is not a function error when updating state when using array of objects

my 'handleGrade' function is giving an error when I try to update the grade value when received.
function Sgpa() {
const [subjects, setSubjects] = useState([
{
name: "subject name",
credits: 3,
points: 0,
grade: "B+"
}
]);
const [sgpa, setSgpa] = useState(0);
function handleAdd() {
setSubjects((prevValues) => [
...prevValues,
{
name: "subject name",
credits: 3,
points: 3,
grade: "B+"
}
]);
}
useEffect(() => {
calcSgpa();
});
function calcSgpa() {
let totalCredits = 0;
let totalPoints = 0;
subjects.map((subject, i) => {
totalCredits += subject.credits;
totalPoints += subject.points;
});
setSgpa((totalCredits * totalPoints) / totalCredits);
}
The error is down below. I'm receiving the correct value from event.target and I think I'm failing to update the value inside my array of objects.
function handleGrade(event, i) {
console.log(event.target.value);
setSubjects(...subjects,{ ...subjects[i] , grade:event.target.value });
console.log(subjects);
}
return (
<>
<h3>Sgpa : {sgpa}</h3>
{subjects.map((subject, i) => {
return (
<SgpaComponent subject={subject} key={i} handleGrade={handleGrade} />
);
})}
<button onClick={handleAdd}>+</button>
</>
);
}
map error happens because,
setSubjects(...subjects,{ ...subjects[i] , grade:event.target.value });
Here, you are setting objects instead of array of objects. "..." will pull everything out of array and you forgot to create a new array while pulling everything out. After that you are trying to map the subjects, where it is not an array anymore.
You can do this is in several ways. One of the best way is instead of changing old state directly, you can copy the old state and update that. Finally return the new updated state. Every setState in react will accept the function, which will give you the old state value.
Code will look like this after changes
function handleGrade(event, i) {
setSubjects(oldSubjects => {
let newSubjects = [...oldSubjects];
newSubjects[i] = {
...newSubjects[i],
grade: event.target.value
};
return newSubjects;
});
}
Hope it answered your question.

javascript string comparison issue in array.filter()

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();

Is there a find function in JavaScript that returns what your function returns and not the whole item when the value is truthy

I want to return only the matched item, I solved this problem creating my own high order function, I want to solve this in a completely functional way.
Is there any similar javascript function that does what my function is doing? See the examples below, I wrote some Jest based examples to facilitate what I am expecting.
The function will try to find the value until is different than undefined. If this kind of function does not exist what you guys think of trying implementing it on JavaScript, maybe making a tc39 proposal? Anyone had the same problem as me before?
I know how the Array.prototype.find works and why it does not work when chained to get deep elements.
There are some conditions that I would like to meet:
Return what my function returns and not the whole item if it's truthy.
For performance reasons, when the value is found there is no need to keep looping in the array, in the example below I used the condition anything different from undefined to exit the for loop.
Follow the standard of the others high order functions such as find, map, filter and reduce like this: fn(collection[i], index, collection).
const findItem = (collection, fn) => {
for (let i = 0; i < collection.length; i++) {
const item = fn(collection[i], i, collection)
if (item !== undefined) return item
}
return undefined
}
let groups = [
{ items: [{ id: 1 }, { id: 2 }] },
{ items: [{ id: 3 }, { id: 4 }] },
]
var result = findItem(groups, group =>
findItem(group.items, item => item.id === 4 ? item : undefined))
// works!
expect(result).toEqual(groups[1].items[1])
// Array.prototype.find
var result2 = groups.find(group =>
group.items.find(item => item.id === 4 ? item : undefined))
// returns groups[1], don't work! And I know why it does not work.
expect(result2).toEqual(groups[1].items[1])
Probably horrible, but you could make use of a backdoor in the reduce function that would allow you to exit early on a match
let groups = [
{ items: [{ id: 1 }, { id: 2 }] },
{ items: [{ id: 3 }, { id: 4 }] },
];
const item = groups.slice(0).reduce((val, g, i, arr) => {
for (const item of g.items) {
if (item.id === 4) {
val = item;
arr.splice(1); // exit
}
}
return val;
}, null);
item && console.log(item);
Note - use of slice is to ensure the original array is not mutated

Reduce Object Array to Single Object [duplicate]

I've read this answer on SO to try and understand where I'm going wrong, but not quite getting there.
I have this function :
get() {
var result = {};
this.filters.forEach(filter => result[filter.name] = filter.value);
return result;
}
It turns this :
[
{ name: "Some", value: "20160608" }
]
To this :
{ Some: "20160608" }
And I thought, that is exactly what reduce is for, I have an array, and I want one single value at the end of it.
So I thought this :
this.filters.reduce((result, filter) => {
result[filter.name] = filter.value;
return result;
});
But that doesn't produce the correct result.
1) Can I use Reduce here?
2) Why does it not produce the correct result.
From my understanding, the first iteration the result would be an empty object of some description, but it is the array itself.
So how would you go about redefining that on the first iteration - these thoughts provoke the feeling that it isn't right in this situation!
Set initial value as object
this.filters = this.filters.reduce((result, filter) => {
result[filter.name] = filter.value;
return result;
},{});
//-^----------- here
var filters = [{
name: "Some",
value: "20160608"
}];
filters = filters.reduce((result, filter) => {
result[filter.name] = filter.value;
return result;
}, {});
console.log(filters);
var filters = [{
name: "Some",
value: "20160608"
}];
filters = filters.reduce((result, {name, value}= filter) => (result[name] = value, result), {});
console.log(filters);
Since 2019 (ES2019) you can go with Object.fromEntries() but you need to map to array first.
const filtersObject = Object.fromEntries(filters.map(({ name, value }) => [name, value])

Javascript - Building conditions dynamically

I created a general function called unique() to remove duplicates from a specific array.
However I'm facing a problem: I want to build the conditions dynamically based on properties that I pass to the function.
Ex: Let's suppose that I want to pass 2 properties, so I want to check these 2 properties before "remove" that duplicated object.
Currently I'm using eval() to build this condition "&&", however according to my search it's really a bad practice.
So, my question is:
What's the proper way to do this kind of thing?
Below is my current code:
function unique(arr, ...props) {
const conditions = [];
for (let prop of props) {
conditions.push(`element['${prop}'] === elem['${prop}']`);
}
const condStr = conditions.join(' && ');
return arr.filter((element, index) => {
const idx = arr.findIndex((elem) => {
return eval(condStr);
});
return idx === index;
});
}
const arr1 = [{
id: 1,
name: 'Josh',
description: 'A description'
}, {
id: 2,
name: 'Hannah',
description: 'A description#2'
}, {
id: 1,
name: 'Josh',
description: 'A description#3'
}, {
id: 5,
name: 'Anyname',
description: 'A description#4'
}];
const uniqueValues = unique(arr1, 'id', 'name');
console.log('uniqueValues', uniqueValues);
This question is a bit subjective as far as implementation details, but the better way if you ask me is to pass in a callback function to hand over to filter.
In doing it this way, you can compose the function anyway you see fit. If you have a complex set of conditions you can use composition to build the conditions in the function before you pass it into your unique function https://hackernoon.com/javascript-functional-composition-for-every-day-use-22421ef65a10
A key to function composition is having functions that are composable. A composable function should have 1 input argument and 1 output value.
The hackernoon article is pretty good and goes much further in depth.
this will return a single function that applies all of your preconditions
function unique(arr, callback) {
return arr.filter(callback);
}
const compose = (...functions) => data =>
functions.reduceRight((value, func) => func(value), data)
unique(
[1, 3, 4, 5 ,7, 11, 19teen]
compose(
(someStateCondition) => { /** return true or false **/ },
(result) => { /** return result === someOtherStateCondition **/}
)
)
Use Array#every to compare all properties inline:
function unique(arr, ...props) {
return arr.filter((element, index) => {
const idx = arr.findIndex(
elem => props.every(prop => element[prop] === elem[prop]);
);
return idx === index;
});
}

Categories