I have a movie, I want to show films of the same genres, what am I doing wrong?
My film (find):
{
"id": 1,
"title": "Kill Bill",
"genre_ids": [1, 10, 15]
}
// All films (movies)
{
"id": 2,
"title": "Leon",
"genre_ids": [1, 12, 15]
},
{
"id": 3,
"title": "Spider-man",
"genre_ids": [12, 32, 15]
},
{
"id": 3,
"title": "Marvel cap",
"genre_ids": [20, 38, 1]
},
// My code
return find.map(i => { // My film
return i.genre_ids.map(ids => {
return movies.data.results.filter(movie => { // All films
return movie.genre_ids.filter(idMov => idMov === ids)
})
})
});
Your movie (find) is an object, not an array, it doesn't have a map function to call.
A Solution:
Create a function that can from a single genre and array of movies return an array of matching movies by genre
const matchByGenre = movies => genre =>
movies.filter(movie => movie.genre_ids.includes(genre));
Iterate over the film's genre_ids array for matches. This yields an array of array matches, flatten them with .flat to a single array. The set is used to remove duplicates and have the result returned back to you as array.
const movieSet = Array.from(
new Set(film.genre_ids.map(matchByGenre(movies)).flat())
);
const film = {
id: 1,
title: "Kill Bill",
genre_ids: [1, 10, 15]
};
const movies = [
{
id: 2,
title: "Leon",
genre_ids: [1, 12, 15]
},
{
id: 3,
title: "Spider-man",
genre_ids: [12, 32, 15]
},
{
id: 4,
title: "Marvel cap",
genre_ids: [20, 38, 1]
},
{
id: 5,
title: "The Big Lebowski",
genre_ids: [20, 38, 2]
}
];
const matchByGenre = movies => genre =>
movies.filter(movie => movie.genre_ids.includes(genre));
const movieSet = Array.from(
new Set(film.genre_ids.map(matchByGenre(movies)).flat())
);
console.log(movieSet);
Note: If the syntax for matchByGenre is confusing, it is a curried function taking a movies array and returns a callback function to be used by array::map
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
I have array of objects where I want to filter and combine results based on specific id. This is example:
[
{
id: 1,
items: [
{
id: 10,
values: [11],
},
{
id: 20,
values: [13, 14, 15],
},
],
},
{
id: 2,
items: [
{
id: 10,
values: [12],
},
{
id: 20,
values: [13, 15],
},
],
},
];
And this is expected result:
[
{
id: 10,
values: [11, 12],
},
{
id: 20,
values: [13, 14, 15],
},
];
I also need to filter duplicates. Thanks
Note: What if I want this result?
[
{
// here I want value for id 10 (it will be always one number)
value: 11,
// here I want values for id 20 (array of numbers) => remove possible duplicates
values: [13, 14, 15],
},
{
// here I want value for id 10 (it will be always one number)
value: 12,
// here I want values for id 20 (array of numbers) => remove possible duplicates
values: [13, 15],
},
];
I tried the same approach with Map, but without success. Basically I want to combine values based on ids.
You could do with Array.flatMap to filter all items in single array.
Then recreate the array with Array.reduce and push the value based on id into new value object
And use Array.filter ignore the duplicate values on array
Object.values return only the value of object in array format
Older
const arr = [ { id: 1, items: [ { id: 10, values: [11], }, { id: 20, values: [13, 14, 15], }, ], }, { id: 2, items: [ { id: 10, values: [12], }, { id: 20, values: [13, 15], }, ], }, ];
const res = Object.values(arr.flatMap(({items})=> items)
.reduce((acc,{id,values})=>{
acc[id] = acc[id] ?? {id,values:[]};
//check the object exist or not
let newArr = acc[id]['values'].concat(values);
let valArr = newArr.filter((v,i)=>newArr.indexOf(v) === i)
//remove the duplicates
acc[id]['values'] = valArr
return acc
},{}))
console.log(res)
Updated
const arr = [ { id: 1, items: [ { id: 10, values: [11], }, { id: 20, values: [13, 14, 15], }, ], }, { id: 2, items: [ { id: 10, values: [12], }, { id: 20, values: [13, 15], }, ], }, ];
function filterMethod(arr,value,values){
return arr.map(({items})=> ({
value:detector(items,value)[0],
values:detector(items,values)
}))
}
function detector(items,idVal){
let ind = items.findIndex(({id})=> id === idVal);
return ind > -1 ? items[ind]['values'] : ['']
}
console.log(filterMethod(arr,10,20))
I have an array and I need to filter out keys based on an input string. Only OLD_VAL is static, the rest are dynamic. I tried using the variable but it is not bringing that key
let input = VKORG,VTWEG,MATNR;
let arr = [
{
VKORG: 1100,
VTWEG: 10,
MATNR: 12,
RATE: 0.01,
VALUE: 1,
OLD_VAL: 12,
},
{
VKORG: 2100,
VTWEG: 99,
MATNR: 13,
RATE: 0.11,
VALUE: 11,
OLD_VAL: 12,
},
];
Output:
[
{
VKORG: "1100",
VTWEG: 10,
MATNR: "12",
OLD_VAL: 12,
},
{
VKORG: "2100",
VTWEG: 99,
MATNR: "13",
OLD_VAL: 12,
},
];
Code tried
let filterResults = results.map(({ OLD_VAL,input }) => ({ OLD_VAL, input }))
Assuming input is an array of strings, you can use Object.entries and create an object at each iteration consisting of the key-value pairs where keys are obtained from the input.
const input = ['VKORG', 'VTWEG', 'MATNR']
const arr = [{
VKORG: 1100,
VTWEG: 10,
MATNR: 12,
RATE: 0.01,
VALUE: 1,
OLD_VAL: 12,
},
{
VKORG: 2100,
VTWEG: 99,
MATNR: 13,
RATE: 0.11,
VALUE: 11,
OLD_VAL: 12,
}
]
const result = arr.map(el => Object.fromEntries(input.map(key => [key, el[key]]).concat([['OLD_VAL', el.OLD_VAL]])));
console.log(result);
If the input isn't an array of strings but is a string('VKORG,VTWEG,MATNR') then you can split it and use the above logic.
const input = 'VKORG,VTWEG,MATNR';
const inputArr = input.split(',');
const arr = [{
VKORG: 1100,
VTWEG: 10,
MATNR: 12,
RATE: 0.01,
VALUE: 1,
OLD_VAL: 12,
},
{
VKORG: 2100,
VTWEG: 99,
MATNR: 13,
RATE: 0.11,
VALUE: 11,
OLD_VAL: 12,
}
]
// using a spread operator instead of concat
const result = arr.map(el => Object.fromEntries([
...inputArr.map(key => [key, el[key]]), ['OLD_VAL', el.OLD_VAL]
]));
console.log(result);
You can do this with either way :
Good old for loop
const newArr = [];
for(let obj of arr) {
let newObj = {}
for(let key of input) {
console.log(key)
newObj[key] = obj[key]
}
newArr.push(newObj);
}
Or using map and reduce methods of the Array interface:
arr.map( e => input.reduce((acc, key) => {
acc[key] = e[key];
return acc;
},{}))
PS: dont forget that object keys are strings so your input variable should be :
const input = ['VKORG', 'VTWEG', 'MATNR']
I need to compare id objects array from a firstArray and array numbers (secondArray) and return a new array with objects from the first array which id number exists in the second array.
So at the end, I want a new array with objects with id 39 and 41.
Actually I find something like this:
const result = arr2.filter(o => arr1.find(x => x.id === o));
const arr1 =
"blocks": [
{
"id": 1,
"functions": [ 0, 1 ]
},
{
"id": 39,
"functions": [ 0, 1, 3, 4 ]
},
{
"id": 41,
"functions": [ 0, 1 ]
}
]
const arr2 = [39, 41]
You can use includes() function during filtering. Includes() works like in array function.
const arr1 =
[ {
"id": 1,
"functions": [ 0, 1 ] },
{
"id": 39,
"functions": [ 0, 1, 3, 4 ]
},
{
"id": 41,
"functions": [ 0, 1 ]
}
]
const arr2 = [39, 41]
const result = arr1.filter(o => arr2.includes(o.id));
console.log(result)
You can create a Map to see whether there is an item of Map exists in filtering array. Getting an item from Map method is O(1):
const blocks = [
{
"id": 1,
"functions": [
0,
1
]
},
{
"id": 39,
"functions": [
0,
1,
3,
4
]
},
{
"id": 41,
"functions": [
0,
1
]
}
];
const arr2 = [39, 41];
const arr2Maps = new Map(arr2.map(a=>[a, a]));
const result = blocks.filter(o => arr2Maps.get(o.id));
console.log(result)
In addition, you can use filter and some methods. However, some method has O(n):
const blocks = [
{
"id": 1,
"functions": [
0,
1
]
},
{
"id": 39,
"functions": [
0,
1,
3,
4
]
},
{
"id": 41,
"functions": [
0,
1
]
}
];
const arr2 = [39, 41]
const result = blocks.filter(o => arr2.some(a=> a ==o.id ));
console.log(result)
From a given data structure (json file) I basically need to render a table. Empty rows and/or columns should not render. I'm fairly new to JavaScript and tried different approaches (converting to array and using .map(), reduce(), .filter(), lodash etc.) without success. I don't even know what the best way would be to tackle the problem. (Or what possible search terms would be.)
Neither "row keys" (In example: mo, tu, we, th, fr) nor "column keys" (john, hane, doe) are known and can vary.
Complete example: https://jsbin.com/rafeyasena/edit?js,output
"groupA": {
"mo": { "john": 8, "jane": 5, "doe": null },
"tu": { "john": 8, "jane": 5, "doe": null },
"we": { "john": 5, "jane": 9, "doe": null },
"th": { "john": 6, "jane": 3, "doe": null },
"fr": { "john": null, "jane": null, "doe": null }
}
Possible resulting data structure
const header = ["John", "Jane"];
const content = [
"mo": {[ 8, 5 ]},
"tu": {[ 8, 5 ]},
"we": {[ 5, 9 ]},
"th": {[ 6, 3 ]}
]
Expected result (Front-end, React):
| John | Jane |
---|------|--------
mo | 8 | 5 |
tu | 8 | 5 |
we | 5 | 9 |
th | 6 | 3 |
What I tried so far:
I was able to delete all values of null and the corresponding key, if it doesn't contain keys/values any more (Delete null values in nested javascript objects) - leading me with the challenge to find out all the leftover keys to build the table header. (In the example below this would be only John and Jane - So basically a way to iterate over all keys and log each key that exists at least one time). So my current data looks like this (but I'm not sure if it is the best way):
"groupA": {
"mo": { "john": 8, "jane": 5, },
"tu": { "john": 8, "jane": 5, },
"we": { "john": 5, "jane": 9, },
"th": { "john": 6, "jane": 3, }
}
I would just represent the data as a 2D array (that makes rendering easier):
const columnNames = [""];
const rows = [columnNames];
for(const [rowName, values] of Object.entries(groupA)) {
const row = [rowName];
for(const [columnName, value] of Object.entries(values)) {
let position = columnNames.indexOf(columnName);
if(value === null) continue;
if(position === -1)
position = columnNames.push(columnName) - 1;
row[position] = value;
}
rows.push(row);
}
// just some debugging:
console.log( rows.map(row => row.map(it => (it || "").padStart(10)).join("|")).join("\n") );
I think creating that latter format (with the nulls removed) is a very useful first step. From there you could write something like this to get it into a variant of your target format:
const uniqKeys = (obj) => [... new Set(Object.values(obj).flatMap(Object.keys))]
const transform = (group, headers = uniqKeys(group)) => ({
headers,
content: Object.entries(group).reduce(
(a, [k, v]) => ({...a, [k]: headers.map(h => v[h])}),
{}
)
})
const groupA = {mo: {john: 8, jane: 5}, tu: {john: 8, jane: 5}, we: {john: 5, jane: 9}, th: {john: 6, jane: 3}}
console.log(transform(groupA))
Note that the target is a little different than your request, as your example content isn't legal JS ({[ 8, 5 ]} doesn't make sense) but I think it captures the spirit of it, returning something like:
{
headers: ['john', 'jane'],
content: {
mo: [8, 5],
tu: [8, 5],
we: [5, 9],
th: [6, 3]
}
}
Note that this function is a little more general than the requirements, as you could supply it a list of headers and only extract those from the data.
Take a look at object-scan. It makes this sort of this relatively easy once you wrap your head around how it works. Here is how you'd answer your questions
// const objectScan = require('object-scan');
const isNullObject = (obj) => (
obj instanceof Object
&& !Array.isArray(obj)
&& Object.values(obj).every((e) => e === null)
);
const prune = (data) => objectScan(['**'], {
rtn: 'count',
filterFn: ({ value, parent, property }) => {
if (isNullObject(value)) {
delete parent[property];
return true;
}
return false;
}
})(data);
const stats = { groupA: { mo: { john: 8, jane: 5, doe: null }, tu: { john: 8, jane: 5, doe: null }, we: { john: 5, jane: 9, doe: null }, th: { john: 6, jane: 3, doe: null }, fr: { john: null, jane: null, doe: null } } };
console.log(prune(stats)); // return number of replaces
// => 1
console.log(stats);
/* =>
{ groupA:
{ mo: { john: 8, jane: 5, doe: null },
tu: { john: 8, jane: 5, doe: null },
we: { john: 5, jane: 9, doe: null },
th: { john: 6, jane: 3, doe: null } } }
*/
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.8.0"></script>
Disclaimer: I'm the author of object-scan
So on the lodash docs this is how I understand range, _.range(start, end).
So if I used .range() on an array of JSON objects. Like...
const arr = [
{
name: 'name',
},
];
Lets say I had 20 objects and I did arr._.range(5, 5); I'd get back the 5th JSON object and 5 from there.
So I've created a map function that gives me back a list of JSON objects and I then use range on them, here's what I have:
import R from 'lodash';
const getJsonData = (offset, limit) => (
R.map([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], x => ({
title: `title ${x}`,
subtitle: 'description',
image_url: 'http://some-img-url.com',
date: 'date',
tag: 'tag',
places: [
{
places: 'places',
},
],
}), R.range(offset, (limit + offset)))
);
So I'd call this like getJsonData(5, 5); but it still gives me back the full list of 20 JSON objects.
Am I misunderstanding how range works?
The result you have is correct.
The map function from lodash does not accept a third parameter.
So the range call is not taken in account.
https://lodash.com/docs/4.17.4#map
So, I suggest you to use slice function like this:
import R from 'lodash';
const getJsonData = (offset, limit) => (
R.slice(
R.map([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], x => ({
title: `title ${x}`,
subtitle: 'description',
image_url: 'http://some-img-url.com',
date: 'date',
tag: 'tag',
places: [
{
places: 'places',
},
],
}))
, offset, limit + offset)
);