I have an array, clients, which I want to run array.find() on. This array contains objects, and usually looks something like this:
[ { customId: 'user1', clientId: 'TPGMNrnGtpRYtxxIAAAC' },
{ customId: 'user2', clientId: 'G80kFbp9ggAcLiDjAAAE' } ]
This is where I encounter a problem. I am trying to use find() to see if any object (or part of an object) in the array matches a certain variable, recipient, which usually contains a value like user1. the code I am using to do this is:
function checkID(recipient) {
return recipient;
}
var found = clients.find(checkID);
This always returns the first object in the array. Am I using find() wrong, or is there a better way to do this?
find takes a predicate (a function that returns true if item is a match and false if item is not a match).
const arr = [ { customId: 'user1', clientId: 'TPGMNrnGtpRYtxxIAAAC' },
{ customId: 'user2', clientId: 'G80kFbp9ggAcLiDjAAAE' } ]
const result = arr.find(item => item.customId === 'user1')
// ^^^^^^^^^^^^^^^^^^^^^^^^^
// This should evaluate to true for a match and to false for non-match
The reason you're getting the first item of your array all the time, is because your checkId function is returning something which evaluates to true. So, the first item is evaluated and produces a truthy result, and therefore it gets picked as the first match.
If unfamiliar with the lambda syntax () => {}, then that line is similar to:
const result = arr.find(function (item) { return item.customId === 'user1' })
You are using find wrong.
If recipient contains information about the target value you should name the first param of checkID with a different name. And compare any property of it with recipient.
var found = clients.find(function(element) { return element.prop1 === recipient.anyProp; });
To check the objects in the array for the presence of a certain customId, put the value you're searching for in an object, and pass that object to find():
let clients = [{
customId: "user1",
clientId: "TPGMNrnGtpRYtxxIAAAC"
},
{
customId: "user2",
clientId: "G80kFbp9ggAcLiDjAAAE"
}
];
function checkID(el){
return el.customId === this.param;
}
let found = clients.find(checkID, {param: "user1"});
console.info(found);
Related
i have two arrays, the finished array, which is
const finished = [ 'Pablo', 'mauro', '12/09/2022', '18:00', 'corte cabelo' ]
and another one, calldes users, which is
const users = [
{
name: 'Pablo',
professional: 'mauro',
date: '12/09/2022',
hour: '18:00',
service: 'corte cabelo'
}
]
and im create another one, to see if got a match value
const isValid = users.filter(
(user) =>
user.professional === finished[1] &&
user.date === finished[2] &&
user.hour === finished[2],
);
but even when i have a match the output of the array isValid always are an empty array.
[]
I want that when there is a match, it returns to me in a new array.
What am I doing wrong?
i tried also includes, but want to see if are match inner the obj inside the array
I have a Node.js program that is using Mongo Atlas search indexes and is utilizing the Aggregate function inside of the MongoDB driver. In order to search, the user would pass the search queries inside of the query parameters of the URL. That being said, I am trying to build a search object based on if a query parameter exists or not. In order to build the search object I am currently using object spread syntax and parameter short-circuiting, like so:
const mustObj = {
...(query.term && {
text: {
query: query.term,
path: ['name', 'description', 'specs'],
fuzzy: {
maxEdits: 2.0,
},
},
})
}
This is a shortened version, as there are many more parameters, but you get the jest.
In a MongoDB search query, if you have multiple parameters that must meet a certain criteria, they have to be included inside of an array called must, like so:
{
$search: {
compound: {
must: [],
},
},
}
So, in order to include my search params I must first turn my mustObj into an array of objects using Object.keys and mapping them to an array, then assigning the searches 'must' array to the array I've created, like so:
const mustArr = Object.keys(mustObj).map((key) => {
return { [key === 'text2' ? 'text' : key]: mustObj[key] };
});
searchObj[0].$search.compound.must = mustArr;
What I would like to do is, instead of creating the mustObj and then looping over the entire thing to create an array, is to just create the array using the spread syntax and short-curcuiting method I used when creating the object.
I've tried the below code, but to no avail. I get the 'object is not iterable' error:
const mustArr = [
...(query.term && {
text: {
query: query.term,
path: ['name', 'description', 'specs'],
fuzzy: {
maxEdits: 2.0,
},
},
})
]
In all, my question is, is what I'm asking even possible? And if so, how?
Corrected based on #VLAZ comment:
while spread with array [...(item)], item has to be array (iterable).
When you use short-circuit, the item as below,
true && [] ==> will be `[]` ==> it will work
false && [] ==> will be `false` ==> wont work (because false is not array)
try some thing like (Similar to #Chau's suggestion)
const mustArr = [
...(query.term ? [{
text: {
query: query.term,
path: ['name', 'description', 'specs'],
fuzzy: {
maxEdits: 2.0,
},
},
}] : [])
]
I have two JSON documents that I want to assert equal for Jest unit testing. They should be equal, except the second one has one more key: _id.
Example:
doc1.json
{
username: 'someone',
firstName: 'some',
lastName: 'one',
}
doc2.json
{
_id: '901735013857',
username: 'someone',
firstName: 'some',
lastName: 'one',
}
My code currently looks like this:
const result = await activeDirectoryUserCollection
.findOne({username: testUser1.username});
expect(result).toBe(testUser1);
Obviously this gives the error that they are not equal, just because of that one value.
I'm looking for an alternative to .toBe() that doesn't completely compare the docs, but checks if one is a subset of another. (or something like that).
Alternatively I would appreciate someone to point me to a module that could help me out.
I would checkout Lodash module's .isMatch function.
It performs a partial deep comparison between object and source to determine if object contains equivalent property values.
https://lodash.com/docs/4.17.11#isMatch
Example:
var object = { 'a': 1, 'b': 2 };
_.isMatch(object, { 'b': 2 });
// => true
_.isMatch(object, { 'b': 1 });
// => false
You can iterate through one Object and use the key to assert value in both Objects. Read More for...in
const result = await activeDirectoryUserCollection
.findOne({username: testUser1.username});
for (const prop in testUser1) {
if (testUser1[prop]) {
expect(result[prop]).toBe(testUser1[prop]); // or use .toEqual
}
}
I don't think you need to look outside jest for this. You can use expect.objectContaining(), which is described in the docs as:
expect.objectContaining(object) matches any received object that recursively matches the expected properties. That is, the expected object is a subset of the received object. Therefore, it matches a received object which contains properties that are present in the expected object.
You could use it like:
test('objects', () => {
expect(doc2).toEqual(
expect.objectContaining(doc1)
);
});
I have an user object - I want to generate test for each user property and check if it's the right type. However as typeof array is an object assertion fails on array properties with "AssertionError: expected [ 1 ] to be an object".
I have therefore checked if the property is an array and then generate special test for it. I'm wondering if this is the right approach? I have a feeling I'm misssing something obvious.
Object.keys(pureUser).forEach(property =>{
// since typeof array is an object we need to check this case separately or test will fail with expecting array to be an object
if (Array.isArray(pureUser[property])) {
it(`should have property ${property}, type: array`, function () {
user.should.have.property(property);
});
} else {
it(`should have property ${property}, type: ${(typeof pureUser[property])}`, function () {
user.should.have.property(property);
user[property].should.be.a(typeof pureUser[property]);
});
}
});
pureUser is something like this:
let pureUser = {
username: "JohnDoe123",
id: 1,
categories: [1,2,3,4]
}
User variable is defined elsewhere via got.js
change your test to be pureUser[property].should.be.an.Array or user[property].should.be.an.Array
forEach
The forEach() method calls a provided function once for each element in an array, in order.
let pureUser = {
username: "JohnDoe123",
id: 1,
categories: [1,2,3,4]
}
Object.keys(pureUser).forEach(property =>{
// since typeof array is an object we need to check this case separately or test will fail with expecting array to be an object
if (Array.isArray(pureUser[property])) {
console.log('Yes, it\'s an Array')
//it(`should have property ${property}, type: array`, function () {
// user.should.have.property(property);
//});
} else {
console.log('No, it\'s not an Array')
//it(`should have property ${property}, type: ${(typeof property)}`, function () {
//user.should.have.property(property);
// user[property].should.be.a(typeof pureUser[property]);
//});
}
});
When you use forEach on pureUser, the parameter will be the objects properties, like username, id, etc
let pureUser = {
username: "JohnDoe123",
id: 1,
categories: [1,2,3,4]
}
Object.keys(pureUser).forEach(property =>{
console.log(property);
});
You can also access the array in your forEach function.
arr.forEach(item, index, arr)
When at the top of my server-side code, this works fine and the results produced are correct:
var data_playlists = {};
models.Playlist.findAll({
attributes: ['id', 'name']
}).then(function (playlists){
data_playlists['playlists'] = playlists.map(function(playlist){
return playlist.get({plain: true})
});
addsongs(data_playlists, 1);
addsongs(data_playlists, 2);
addsongs(data_playlists, 3);
});
but when it's inside one of my Express methods, it isn't functioning properly; particularly, the addsongs method is not working as it should.
function addsongs(playlist_object, id_entered){
var arraysongs = [];
models.Playlist.findOne({
attributes: ['id'],
where: {
id: id_entered
}
})
.then(function(playlist) {
playlist.getSongs().then(function (thesongs){
for(var k = 0; k < thesongs.length ; k++){
arraysongs.push(thesongs[k].Songs_Playlists.SongId);
}
playlist_object.playlists[(id_entered - 1)]['songs'] = arraysongs;
});
});
}
I cannot for the life of me figure out why it works when the top segment of code is at the top, but doesn't work when inside my app.get() call.
From your code I have conducted that you want to return playlists (id and name) together with their songs (id). First of all your code will not work because the calls of addsongs(data_playlists, id) are run before data_playlists is filled with data by code above it. Moreover, the addsongs function performs asynchronous operations returning Promises, so calling them one by one will not give expected result. I suppose you can do it completely differently.
I suggest you use include attribute of options object that can be passed to findAll() method. include says which association model you also want to return from current query. In this case you want to return playlists together with their songs (M:M relation according to your code), so you need to include Song model in the query.
function getPlaylistsWithSongs() {
return models.Playlist.findAll({
attributes: ['id', 'name'],
include: [
{
model: models.Song,
as: 'Songs', // depends on how you have declare the association between songs and playlists
attributes: ['id'],
through: { attributes: [] } // prevents returning fields from join table
}
]
}).then((playlistsWithSongs) => {
return playlistsWithSongs;
});
}
Example result of getPlaylistsWithSongs result would be (after translating it to JSON e.g. like playlistsWithSongs.toJSON())
[
{
id: 1,
name: 'playlist #1',
Songs: [
{ id: 1 },
{ id: 2 }
]
}
]
Above code returns all playlists (their id and name) with their songs (only their id). Now in your route resolver you can simply call above function to return the result
app.get('/api/playlists', function (request, response) {
response.setHeader("Content-Type", "application/json; charset=UTF-8");
getPlaylistsWithSongs().then(function(playlistsWithSongs){
response.status(200).send(JSON.stringify(playlistsWithSongs));
});
});
EDIT
In order to simply return array of IDs instead array of objects with id (songs), you need to map the result. There is no simple sequelize way to return array of IDs in such a case.
}).then((playlistWithSongs) => {
let jsonPlaylists = playlistsWithSongs.map((singlePlaylist) => {
// return JSON representation of each playlist record
return singlePlaylist.toJSON();
});
jsonPlaylists.forEach((playlist) => {
// at every playlist record we map Songs to array of primitive numbers representing it's IDs
playlist.songs = playlist.Songs.map((song) => {
return song.id;
});
// when we finish we can delete the Songs property because now we have songs instead
delete playlist.Songs;
});
console.log(jsonPlaylists);
// example output: [{ id: 1, name: 'playlist #1', songs: [1, 2, 3] }]
return jsonPlaylists;
});