Wrap the function into another function - javascript

I have the code that sorts an array of objects by the object property using lodash. It’s pretty simple:
_.sortBy(data.cycles, "id");
However, I discovered that data.cycles[].id might be a string, so I have to convert each property to number before passing it to _.sortBy. Is there any elegant way of doing it? I think I should combine _.property and parseInt somehow, but I don’t know how.
data.cycles might look like this:
[
{
"id": "20",
"name": "John"
},
{
"id": "15",
"name": "Sarah"
},
{
"id": "158",
"name": "Bill"
},
{
"id": "-6",
"name": "Jack"
},
{
"id": "59",
"name": "Bob"
}
]

I would compose a sortBy() callback as follows:
var collection = [
{ id: '20', name: 'John' },
{ id: 15, name: 'Sarah' },
{ id: 158, name: 'Bill' },
{ id: '-6', name: 'Jack' },
{ id: 59, name: 'Bob' }
];
_.sortBy(collection, _.flow(_.property('id'), parseInt));
// →
// [
// { id: '-6', name: 'Jack' },
// { id: 15, name: 'Sarah' },
// { id: '20', name: 'John' },
// { id: 59, name: 'Bob' },
// { id: 158, name: 'Bill' }
// ]
The flow() function is useful when you want to compose a callback function out of many functions, it just forwards the output to the next function in line. The property() function returns a function that gets the specified property name from it's argument. This is the id, and that gets passed to parseInt().
We're essentially mapping and sorting at the same time. This has an efficiency advantage, in addition to being a one-liner: it doesn't have to iterate over the whole collection twice. This isn't a big deal with smaller collections, but with larger collections, the impact is noticeable.

You might want to run map first and modify id, then do a sort.
In vanilla JS, it would look something like:
function mapFn(item){
item.id = parseInt(item.id, 10);
return item;
}
var sortedCycles = data.cycles.map(mapFn).sort(sortFn);
In lodash, you could have:
var sortedCycles = _.sortBy(_.map(data.cycles, mapFn), 'id');
// or
var sortedCycles = _.chain(data.cycles).map(mapFn).sortBy('id').value()

Related

Edit multiple objects in array using mongoose (MongoDB)

So I tried several ways, but I can't, I can modify several objects with the same key but I can't modify any with different keys, if anyone can help me is quite a complex problem
{
id: 123,
"infos": [
{ name: 'Joe', value: 'Disabled', id: 0 },
{ name: 'Adam', value: 'Enabled', id: 0 }
]
};
In my database I have a collection with an array and several objects inside which gives this.
I want to modify these objects, filter by their name and modify the value.
To give you a better example, my site returns me an object with the new data, and I want to modify the database object with the new object, without clearing the array, the name key never changes.
const object = [
{ name: 'Joe', value: 'Hey', id: 1 },
{ name: 'Adam', value: 'None', id: 1 }
];
for(const obj in object) {
Schema.findOneAndUpdate({ id: 123 }, {
$set: {
[`infos.${obj}.value`]: "Test"
}
})
}
This code works but it is not optimized, it makes several requests, I would like to do everything in one request, and also it doesn't update the id, only the value.
If anyone can help me that would be great, I've looked everywhere and can't find anything
My schema structure
new Schema({
id: { "type": String, "required": true, "unique": true },
infos: []
})
I use the $addToSet method to insert objects into the infos array
Try This :
db.collection.update({
id: 123,
},
{
$set: {
"infos.$[x].value": "Value",
"infos.$[x].name": "User"
}
},
{
arrayFilters: [
{
"x.id": {
$in: [
1
]
}
},
],
multi: true
})
The all positional $[] operator acts as a placeholder for all elements in the array field.
In $in you can use dynamic array of id.
Ex :
const ids = [1,2,..n]
db.collection.update(
//Same code as it is...
{
arrayFilters: [
{
"x.id": {
$in: ids
}
},
],
multi: true
})
MongoPlayGround Link : https://mongoplayground.net/p/Tuz831lkPqk
Maybe you look for something like this:
db.collection.update({},
{
$set: {
"infos.$[x].value": "test1",
"infos.$[x].id": 10,
"infos.$[y].value": "test2",
"infos.$[y].id": 20
}
},
{
arrayFilters: [
{
"x.name": "Adam"
},
{
"y.name": "Joe"
}
],
multi: true
})
Explained:
You define arrayFilters for all names in objects you have and update the values & id in all documents ...
playground

How to relate two (or more) objects through a property (in javascript)?

Let's say I have these two objects:
let person1 = {name: "Charlie", age: 65, childrenNames:["Ruth", "Charlie Jr."] parentNames: ["Dolph", "Grace"]};
let person2 = {name: "Charlie Jr.", age: 34, childrenNames:[] parentNames: ["Charlie", "Grace"]};
Now let's say I want to express the fact that person1 is person2's father and, consequently, that person2 is person1's son. That is, the "Charlie Jr" in the person1's childrenNames property is person2 and the "Charlie" in the person2's parentNames property is person1.
How could I achieve this? I don't see how embedding one object inside the other would solve the problem, but simply replicate it. Is there a way to make a property inside an object a sort of identifier for another object?
Thanks so much!
For example if you want to know if someone is the child of person 1, you can do something like this:
person1.childrenNames.forEach((childrenName) => {
if(childrenName=== person2.name) {
console.log(person1.name + ' is the parent of + person2.name);
});
Also you can do a nested function so you can check if the person if the parent of multiple persons.
Why not add relationship indexes ? combine your people, to 1 people array.
Iterate and add instead of parentNames, parentIndexes. This way, instead of looking for parents or sons by names, you have the index.
Note, to make my example simple, I am only doing parent to son relationship. You can easily add a son to parent relationship using the exact same logic.
Example
if (peopleArray[i].parentsIndex.length > 0) {
// get first parent
//peopleArray[peopleArray[i].parentsIndex[0]];
}
Modify your object.
let peopleArray = [
{
name: "Charlie",
age: 65,
parentNames: ["Dolph", "Grace"]
},
{
name: "Grace",
age: 65,
parentNames: ["Dolph", "Grace"]
},
{
name: "Dolph",
age: 65,
parentNames: ["ADSGS", "Grace"]
}
];
peopleArray = peopleArray.map(callback.bind(null));
function callback(item) {
item["parentsIndex"] = [];
for (let i = 0; i < item.parentNames.length; i++) {
let parentObj = peopleArray.find(x => x.name === item.parentNames[i]);
if (parentObj !== undefined) {
item["parentsIndex"].push(peopleArray.indexOf(parentObj));
}
}
return item;
}
console.log(peopleArray);
// use case
if (peopleArray[0].parentsIndex.length > 0) {
// get first parent
//peopleArray[peopleArray[0].parentsIndex[0]];
}
I guess it depends on how complicated your scenario would be and what you would like to achieve, but say you add an extra table for relations. This table could hold information on the type of relation 2 persons share, and could then be used to look up that data.
For example, if we have 4 persons, from which 2 are parents (Charlie & Grace) and 1 is the son (Charlie Jr), we could form a relation table as below.
We don't need to indicate that Charlie Jr is a son, as he we already know the parents of the child.
const familyDb = {
persons: [
{ id: 1, name: 'Charlie', age: 68 },
{ id: 2, name: 'Grace', age: 64 },
{ id: 3, name: 'Charlie Jr', age: 34 },
{ id: 4, name: 'Grace', age: 36 }
],
relations: [
{ id: 1, person1: 1, person2: 2, type: 'spouse', from: 1970, till: null },
{ id: 2, person1: 3, person2: 4, type: 'spouse', from: 2010, till: null },
{ id: 3, person1: 1, person2: 3, type: 'parent' },
{ id: 3, person1: 2, person2: 3, type: 'parent' }
]
};
function getParents( person ) {
return familyDb.relations.filter( relation => relation.person2 === person.id && relation.type === 'parent' );
}
function getChildren( person ) {
return familyDb.relations.filter( relation => relation.person1 === person.id && relation.type === 'parent' );
}
console.log( getParents( familyDb.persons[2] ) );
console.log( getChildren( familyDb.persons[0] ) );
So the above code kinda takes a rudimentary approach to this, you have:
a unique id identifying a person (name matching would be hard in your example as Grace is both the mom of Charlie & Charlie Jr)
a table identifying a relation of some type between two persons
After that you just need a way to look up the information from your dataset and you have a way to get started

Omitting one or multiple values in javascript using lodash

I have a complex structure and I want to omit some properties from this structure for final value
let ListofWorlds = {
listOfCountries: [{
add: [{
id: 1,
updated: {
areacode: 123,
city: {
city: {'Austrailia'},
houses: {1000}
}
}
}], remove: []
}]
}
I want to omit city property from this structure and need this
let ListofWorlds = {
listOfCountries: [{
add: [{
id: 1,
updated: {
areacode: 123
}
}], remove: []
}]
}
This is what I have tried
let newListOfWorls = _.map(ListofWorlds, function (worlds) {
return _.omit(worlds, ['city']); })
Appreciate the help and knowledge
This is what i have tried.
let ListofWorlds = {
listOfCountries: [{
add: [{
id: 1,
updated: {
areacode: 123,
city: {
city: 'Austrailia',
houses: 1000
}
}
}], remove: []
}]}
const newList = ListofWorlds.listOfCountries.map(arr=>{
arr.add.forEach((item,index)=>{
arr.add[index] = _.omit(item,'updated.city')
})
return arr
})
Probably not the best way to do it, but hey it works, and why your code doesn't work probably you mapped an Object ListofWorlds and you need to be specific which field you want to be omitted

Build JS arrays by key into one - find a best solution

What's the best solution to mapping 2 multiple arrays to build one by key?
I have 1 array with users who have their profile data like
var users = [{id:5, name:'Alex'}, {id:17, name:'Tom'}, {id:11, name:'John'}];
Also I have another one array of cars with key user_id To determine which machine belongs to which user.
var cars = [{id:333, name:'Nissan', user_id:11}, {id:444, name:'Toyota', user_id:17}, {id:555, name:'BMW', user_id:999}];
So we can see that Tom have Toyota and John have Nissan.
So result should be
a new array with mapped result
[{
"profile": {
"id": 17,
"name": "Tom"
},
"car": {
"id": 444,
"name": "Toyota",
"user_id": 17
}
}, {
"profile": {
"id": 11,
"name": "John"
},
"car": {
"id": 333,
"name": "Nissan",
"user_id": 11
}
}]
My solution is use forEach throw users and sub forEach throw cars and there compare user.id with car.user_id
https://jsfiddle.net/r7qwke1f/37/
You could use a two loop approach instead of a nested loop approach by collecting first all users in a hash table anbd then iterate all cars and if a user is available, then create a new result set.
var users = [{ id: 5, name: 'Alex' }, { id: 17, name: 'Tom' }, { id: 11, name: 'John' }],
cars = [{ id: 333, name: 'Nissan', user_id: 11 }, { id: 444, name: 'Toyota', user_id: 17 }, { id: 555, name: 'BMW', user_id: 999 }],
hash = {},
result = [];
users.forEach(function (user) {
hash[user.id] = user;
});
cars.forEach(function (car) {
if (hash[car.user_id]) {
result.push({ profile: hash[car.user_id], car: car });
}
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Another solution
const mappedUsersCars = users.map((user) => ({
profile: user,
car: cars.filter((car) => car.user_id === user.id)[0]
}))
You can use reduce() and find() methods to get desired result.
var users = [{id:5, name:'Alex'}, {id:17, name:'Tom'}, {id:11, name:'John'}];
var cars = [{id:333, name:'Nissan', user_id:11}, {id:444, name:'Toyota', user_id:17}, {id:555, name:'BMW', user_id:999}];
var r = users.reduce(function(r, e) {
var car = cars.find(a => a.user_id == e.id);
if(car) r.push({profile: e, car: car});
return r;
}, [])
console.log(r)
There are basically two methods you would want to use. You want to map the users to the cars, so you want to find a car for the user you are referring to
const result = users.map((user) => {
const car = cars.find(car => car.user_id === user.id);
return {
profile: user,
car,
}
})

Most efficient way to search through an object and array locating match

i have 2 object/arrays:
var objA = {
Red Chair : "DC10291",
USBDongle : "USKI82322",
}
var arrayB = [
{
field: "Yellow Banana",
id: "Yellow Banana"
},
{
field: "Red Chair",
id: "Red Chair"
},
{
field: "Garden",
id: "Garden"
}
]
What i am trying to do is, that if a KEY from objA, e.g. Red Chair, is present in arrayB, then remove it from arrayB.
I have done this:
var arrayClone = _.cloneDeep(arrayB);
var removeThese = [];
Object.keys(arrayClone).forEach(function(p) {
removeThese.push(p)
});
removeThese.forEach(function(remove) {
arrayB.forEach(function(item) {
if(item.id === remove) {
delete objA[remove];
}
});
});
The above works as expected, however is this the most effieicnt? Reasone i ask is because looping throuhg and array within an array loop doesnt feel the best practice? And will have performance impact
You can simply filter it, like this
_.filter(arrayB, obj => !objA.hasOwnProperty(obj.field))
// [ { field: 'Yellow Banana', id: 'Yellow Banana' },
// { field: 'Garden', id: 'Garden' } ]
This uses ES2015's Arrow function syntax. You can write the same with a normal function like this
arrayB.filter(function(obj) {
return !objA.hasOwnProperty(obj.field);
});
// [ { field: 'Yellow Banana', id: 'Yellow Banana' },
// { field: 'Garden', id: 'Garden' } ]
We are basically filtering out all the objects whose field value is a key in objA.
If you would like to keep the original arrayB and get a reduced version of it according to your condition then Array.prototype.reduce() does that with O(n) time complexity. However if you would like to perform this operation in place then Array.prototype.reduceRight() does that with O(n) time complexity.
var objA = {
"Red Chair" : "DC10291",
"USBDongle" : "USKI82322",
},
arrayB = [
{
field: "Yellow Banana",
id: "Yellow Banana"
},
{
field: "Red Chair",
id: "Red Chair"
},
{
field: "Garden",
id: "Garden"
}
],
arrayC = arrayB.reduce((p,c) => !objA[c.field] ? p.concat(c) : p, []);
console.log(arrayC);
arrayB.reduceRight((p,c,i,a) => (p[c.field] && a.splice(i,1),p),objA);
console.log(arrayB);

Categories

Resources