Filter an array by equal id - javascript

I got an array like this and I need to filter this array with the same user id with all the occurrences with the service_id corresponding to user_id:
Array [
Object {
"service_id": 14,
"user_id": 56,
},
Object {
"service_id": 19,
"user_id": 59,
},
Object {
"service_id": 18,
"user_id": 56,
},
Object {
"service_id": 18,
"user_id": 56,
},
]
And I need to filter the array like this:
Array [
Object {
"user_id": 56,
[
{"service_id":14},
{"service_id": 18}
]
},
Object {
"user_id": 59,
[
{"service_id": 19},
]
},
]

var array = [{"service_id": 14,"user_id": 56,},{"service_id": 19,"user_id": 59,},
{"service_id": 18,"user_id": 56,},{"service_id": 18,"user_id": 56}]
const groupByUserId = (array, key) => {
return array.reduce((result, currentValue) => {(
//Create a new array as key if there is not found
result[currentValue[key]] = result[currentValue[key]] || []).push(currentValue);
return result;
}, {}); // empty object after initialization
};
const grouped = groupByUserId(array, 'user_id');
console.log(grouped)
In Javascript your array should look like this:
var array = [{"service_id": 14,"user_id": 56},{"service_id": 19,"user_id": 59},
{"service_id": 18,"user_id": 56},{"service_id": 18,"user_id": 56}]
There is an assumption to be made here to identify user_id as key in the new grouped array in order to populate the user's similar service_id inside its corresponding array.
const groupByUserId = (array, key) => {
return array.reduce((result, currentValue) => {(
//Create a new array as key if there is not found
result[currentValue[key]] = result[currentValue[key]] || []).push(currentValue);
return result;
}, {}); // empty object after initialization
};
const grouped = groupByUserId(array, 'user_id');
console.log(grouped)

Your output array must be as the below example I suppose.
You can use Array.reduce for that.
const data = [
{
"service_id": 14,
"user_id": 56,
},
{
"service_id": 19,
"user_id": 59,
},
{
"service_id": 18,
"user_id": 56,
},
{
"service_id": 18,
"user_id": 56,
},
]
const output = data.reduce((acc, curr) => {
const node = acc.find((item) => item.user_id === curr.user_id);
if (node) {
node.list.push({service_id: curr.service_id})
} else {
acc.push({
user_id: curr.user_id,
list: [{service_id: curr.service_id}]
})
}
return acc;
}, []);
console.log(output)

Related

Sort object elements by string, where each string is an array

I have an object where each string contains an array:
var users = {
name : ["joe" , "tim" , "bob" , "joe"],
age : [23 , 22 , 21 , 20 ],
other : ["AA" , "BB" , "CC" , "DD"]
}
How can I sort these elements, for example by age, so I get this as a result?
var users = {
name : ["joe" , "tim" , "bob" , "joe"],
age : [ 20 , 21 , 22 , 23 ],
other : ["DD" , "CC" , "BB" , "AA" ]
}
I nserted a duplicate name on purpose, since it should work in cases like these (should keep the relationship positions).
Note: In case someone else has this same issue, after reading the answers I now recommend changing the data structure to:
var users = [ // note this is now an array
name : {"joe" , "tim" , "bob" , "joe"},
age : {23 , 22 , 21 , 20 },
other : {"AA" , "BB" , "CC" , "DD"}
]
This way, more simple approaches like .sort can be used.
First, we can take the data you are starting with and create a new array of objects where we map each individual to their assigned age, and then we sort that array of objects by its age property value:
let users = {
name: ["joe", "tim", "bob", "joe"],
age: [23, 22, 21, 20]
};
const associateUserDetails = users => Array(users.name.length).fill().map((e,i) => ({name: users.name[i], age: users.age[i]})).sort((a,b) => a.age - b.age);
console.log(associateUserDetails(users));
/* -> [
{ name: "joe", age: 20 },
{ name: "bob", age: 21 },
{ name: "tim", age: 22 },
{ name: "joe", age: 23 }
]
*/
Once we this array of users sorted by their age, all that's left to do is split the array of objects back up into two nested arrays for name and age, like this:
let users = {
name: ["joe", "tim", "bob", "joe"],
age: [23, 22, 21, 20]
};
const associateUserDetails = users => Array(users.name.length).fill().map((e,i) => ({name: users.name[i], age: users.age[i]})).sort((a,b) => a.age - b.age);
const splitByDetail = users => users.flatMap(user => Object.entries(user)).reduce((a,c) => (a[c[0]] ? a[c[0]].push(c[1]) : a[c[0]] = [c[1]], a), {});
console.log(splitByDetail(associateUserDetails(users)));
/* -> {
name: ["joe", "bob", "tim", "joe"],
age: [20, 21, 22, 23]
}
*/
Now let's combine this all together:
let users = {
name: ["joe", "tim", "bob", "joe"],
age: [23, 22, 21, 20]
};
const sortUsersByAge = users => Array(users.name.length).fill().map((e,i) => ({name: users.name[i], age: users.age[i]})).sort((a,b) => a.age - b.age).flatMap(user => Object.entries(user)).reduce((a,c) => (a[c[0]] ? a[c[0]].push(c[1]) : a[c[0]] = [c[1]], a), {});
console.log(sortUsersByAge(users));
/* -> {
name: ["joe", "bob", "tim", "joe"],
age: [20, 21, 22, 23]
}
*/
BONUS
Now for a bonus— let's say you have an object like the one you provided and…
are working with many more user details, maybe even a variable number of details, and you would rather list them all explicitly
want the ability to sort by any of the user details listed, name (alphabetical sorting), age (numerical sorting), ID number (numerical sorting), or some other property (default to alphabetical sorting unless all values are numbers)
would like to add a boolean true/false switch to quickly reverse the order of any sorting function like sortUsers(users, true) where the second parameter would reverse each final array's state after sorting
you want to use this function as an Object.prototype method, like users.sortBy('age') instead of sortObject(users, 'age')
Let's make all of those improvements and put it all together into one prototype function Object.sortBy():
let users = {
name: ["joe", "tim", "bob", "joe"],
age: [23, 22, 21, 20],
color: ["blue", "green", "red", "yellow"]
};
Object.prototype.sortBy = function(sortBy, descending = false) { return sortBy && Object.keys(this).includes(sortBy) ? [Array(this[Object.keys(this)[0]].length).fill().map((e,i) => Object.fromEntries(Object.keys(this).map(key => [key, this[key][i]])))].map(obj => obj.sort((a,b) => obj.every(user => typeof user[sortBy] === "number") ? a[sortBy] - b[sortBy] : (a[sortBy] > b[sortBy] ? 1 : -1)))[0].flatMap(user => Object.entries(user)).reduce((a,c) => (a[c[0]] ? a[c[0]].push(c[1]) : a[c[0]] = [c[1]], a), {}) : this };
users.sortBy('age');
/* -> {
name: ["joe", "bob", "tim", "joe"],
age: [20, 21, 22, 23],
color: ["yellow", "red", "green", "blue"]
}
*/
users.sortBy('name');
/* -> {
name: ["bob", "joe", "joe", "tim"],
age: [21, 20, 23, 22],
color: ["red", "yellow", "blue", "green"]
}
*/
users.sortBy('color', true); // true here will sort colors by name in descending order
/* -> {
name: ["joe", "bob", "tim", "joe"],
age: [20, 21, 22, 23],
color: ["yellow", "red", "green", "blue"]
}
*/
One way is group the user names by age in a Map or object keyed by ages.
Then after sorting the ages, map the ages array to names array plucking the name values from the groupBy object/Map
var users = {
name: ["bob", "joe", "tim", "bob", "joe"],
age: [20, 23, 22, 21, 20]// added duplicate age also
}
// Map using ages as keys , empty array for values
const grouped = new Map(users.age.map((age, i) => [age, []]))
// push each name to appropriate age array in map
users.name.forEach((name, i) => grouped.get(users.age[i]).push(name));
const sortedAges = users.age.slice().sort((a, b) => a - b),
// rebuild names array from items in Map
sortedNames = sortedAges.map(age => grouped.get(age).shift());
const sortedUsers = {
name:sortedNames,
age: sortedAges
}
for(prop in sortedUsers){
console.log(JSON.stringify(sortedUsers[prop]))
}
Explanation Provided all the values in the object are arrays.
Firstly, duplicate the original array to prevent changing the original object.
Using a while loop to find the index of the smallest passed property using the Math.min function.
Splicing the index from all the keys of the passed object and pushing it into the new object.
let users = {name:["joe", "tim", "bob", "joe"], age:[23, 22, 21, 20], other : ["AA", "BB", "CC", "DD"]};
function sortObject(obj, prop) {
obj = {...obj}; // To avoid changing the initial object
const keys = Object.keys(obj);
const newObj = keys.reduce((a, b) => {
a[b] = [];
return a;
}, {});
while (obj[prop].length) {
const index = obj[prop].findIndex(age => age == Math.min(...obj[prop]));
keys.forEach(key => newObj[key].push(obj[key].splice(index, 1)[0]));
}
return newObj;
}
console.log(sortObject(users, 'age'));
Now found this:
/**
* Sorts all arrays together with the first. Pass either a list of arrays, or a map. Any key is accepted.
* Array|Object arrays [sortableArray, ...otherArrays]; {sortableArray: [], secondaryArray: [], ...}
* Function comparator(?,?) -> int optional compareFunction, compatible with Array.sort(compareFunction)
*/
function sortArrays(arrays, comparator = (a, b) => (a < b) ? -1 : (a > b) ? 1 : 0) {
let arrayKeys = Object.keys(arrays);
let sortableArray = Object.values(arrays)[0];
let indexes = Object.keys(sortableArray);
let sortedIndexes = indexes.sort((a, b) => comparator(sortableArray[a], sortableArray[b]));
let sortByIndexes = (array, sortedIndexes) => sortedIndexes.map(sortedIndex => array[sortedIndex]);
if (Array.isArray(arrays)) {
return arrayKeys.map(arrayIndex => sortByIndexes(arrays[arrayIndex], sortedIndexes));
} else {
let sortedArrays = {};
arrayKeys.forEach((arrayKey) => {
sortedArrays[arrayKey] = sortByIndexes(arrays[arrayKey], sortedIndexes);
});
return sortedArrays;
}
}
Example:
let people = ["john", "benny", "sally", "george"];
let peopleIds = [10, 20, 30, 40];
sortArrays([people, peopleIds]);
[["benny", "george", "john", "sally"], [20, 40, 10, 30]] // output
sortArrays({people, peopleIds});
{"people": ["benny", "george", "john", "sally"], "peopleIds": [20, 40, 10, 30]} // output
Source: https://gist.github.com/boukeversteegh/3219ffb912ac6ef7282b1f5ce7a379ad

Filter Dynamic keys based on input

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']

Find the same elements of an array from other arrays?

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

How can I get objects from array with specific id compares from another array numbers

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)

How to convert an object of parallel lists to a list of objects?

I've got an object like
var data = [
{
"Name": ["Jane", "Peter", "Jenny"],
"Id": [1, 2, 3],
"Years": [16, 17, 18]
}
];
But I want to put it to a react table which need another format like it
var data1 = [
{
"Name": "Jane",
"Id": 1,
"Years": 16,
},
{
"Name": "Peter",
"Id": 2,
"Years": 17,
},
{
"Name": "Jenny",
"Id": 3,
"Years": 18,
}
]
How can I conversion it with JSX?
Here is the pretty much straight forward implementation for your problem. For iteration, I have considered the length of Name array inside the object.
var data = [{
"Name": ["Jane", "Peter", "Jenny"],
"Id": [1, 2, 3],
"Years": [16, 17, 18]
}];
var data1 = [ ];
var iterations = data[0].Name.length;
var requiredData = data[0];
var keyArray = Object.keys(requiredData);
for ( var i = 0; i < iterations; i++ ) {
tempObj = { };
for (var key of keyArray) {
tempObj[key] = requiredData[key][i];
}
data1.push(tempObj);
}
console.log('data1 = ', data1)
I'd find out the maximum property length (in case they aren't all the same), create an array of that size, iterate in a for-loop, create objects using the data keys and matching index positions (null if there's no match) and push them into the array.
const data = [{
"Name": ["Jane", "Peter", "Jenny"],
"Id": [1, 2, 3],
"Years": [16, 17, 18]
}]
const interestingData = data[0]
const keys = Object.keys(interestingData)
const maxItems = keys.reduce((count, key) => Math.max(count, interestingData[key].length), 0)
const transformed = new Array(maxItems)
for (var i = 0; i < maxItems; i++) {
transformed[i] = keys.reduce((obj, key) => Object.assign(obj, {
[key]: interestingData[key][i] || null // default value if no matching index
}), Object.create(null))
}
console.info(transformed)

Categories

Resources