Using underscore _.extend to avoid overwriting nested objects? - javascript

var obj = {
customerInfo: {
fname: "Tom",
lname: "Smith",
age: 23,
height: 160
},
car: {
color: "red",
numWheels: 4
}
};
I would like to modify this object into:
var obj = {
customerInfo: {
fname: "Sally",
lname: "Adams",
mname: "Tully",
age: 23,
height: 160
},
car: {
color: "red",
numWheels: 4
}
};
However, if I do
_.extend(obj, {
customerInfo: {
fname: "Sally",
lname: "Adams",
mname: "Tully"
}
});
the object becomes
{
customerInfo: {
fname: "Sally",
lname: "Adams",
mname: "Tully"
},
car: {
color: "red",
numWheels: 4
}
};
and the age and height have been wiped out.
What do I need to do to preserve data that's nested?
And what about updating more complex objects?
http://jsfiddle.net/j4L18p7k/2/#share

There are better ways of achieving this deep merge behavior you're actually looking for...
You could use Jquery's $.extend API as documented here: http://api.jquery.com/jquery.extend/
I've put together this little example to match your code sample since I believe you that this kind of generic approach to merging is what you're actually looking for, right?
var o = {
customerInfo: {
fname: "Tom",
lname: "Smith",
age: 23,
height: 160,
paymentMethods: {
paypal: {
username: "sally"
}
}
},
car: {
color: "red",
numWheels: 4
}
};
// this wipes out more deeply nested things (paypal disappears)
var merge = $.extend(true, {} , o.customerInfo, {
"fname": "Sally",
lname: "Adams",
mname: "Tully",
paymentMethods: {
visa: {
lastFour: "5555"
}
}
});
console.dir(merge);
http://jsfiddle.net/hrqbyd5c/9/
However pay attention to the JQuery docs
On a deep extend, Object and Array are extended, but object wrappers on primitive types such as String, Boolean, and Number are not. Deep-extending a cyclical data structure will result in an error.
For needs that fall outside of this behavior, write a custom extend method instead, or use a library like lodash.
Check out lodash's merge API documentation https://lodash.com/docs#merge
Hope this helped some more...

Extend customerInfo directly. Underscore .extend() replaces any key you mention, in this case removing any previous nested keys that aren't in your new object.
_.extend(obj.customerInfo, {
fname: "Sally",
lname: "Adams",
mname: "Tully"
});

Related

Javascript Object method value not showing with console.log of the object

I'm a bit confused with objects in JavaScript...
I wrote an object:
const gix = {
firstName: "John",
lastName: "Johnson",
yearOfBirth: 2000,
profession: "IT",
friends: ["Mark", "Luke", "John"],
driversLicence: true,
age: function () {
this.calcAge = 2022 - this.yearOfBirth;
return this.calcAge;
},
};
gix.age();
console.log(gix);
Why is the console log of the whole object not showing the calculated value but is showing age: f()
Considering your use-case, you could replace the method with a getter, which gets evaluted each time the object is referenced:
const gix = {
firstName: "John",
lastName: "Johnson",
yearOfBirth: 2000,
profession: "IT",
friends: ["Mark", "Luke", "John"],
driversLicence: true,
get age() {
return 2022 - this.yearOfBirth;
},
};
You would either want to capture the return value of the function or call it so:
console.log(gix.age());
Think of age as a pointer to the function...
When you're console logging a function it wont execute it, It will just show the contents of the function, IF you need to see the value then you need to call the function and log the output
console.log(gix.age());
If you check the log, it actually added calcAge as you already called age before. If you want to use a computed property calcAge from yearOfBirth, you can use Object getters
const gix = {
firstName: "John",
lastName: "Johnson",
yearOfBirth: 2000,
profession: "IT",
friends: ["Mark", "Luke", "John"],
driversLicence: true,
get calcAge () { return 2022 - this.yearOfBirth; },
};
console.log(gix.calcAge);

Algolia: search for multiples values in each fields with AND clause

I would like know if i can query different values in each field
returning values that only match with the search like AND condition. I tried use search in multpile indexes, but it return values like OR condition.
Example:
my index contains:
[
{
firstName: 'Alisson',
lastName: 'Oliveira',
},
{
firstName: 'Alex',
lastName: 'Oliver',
},
{
firstName: 'Daniel',
lastName: 'Costa',
}
]
my query would be:
index.search(query: { firstName: 'Al', lastName: 'Oliv' })
the response espected:
[
{
firstName: 'Alisson',
lastName: 'Oliveira',
},
{
firstName: 'Alex',
lastName: 'Oliver',
}
]
Someone know if is possible in the algolia?
Based on your example it sounds like your end-user would type the query: "Al Oliv" and you would expect the two records shown above to return.
To do this, you would change the default settings from prefixLast to prefixAll. You can do this in the Dashboard:
By default Algolia only uses the last word as a prefix. If you make the change above the query will return the expected results:

Ramda JS: How to perform a map where I call R.replace for a given property on each object?

Given the following data:
const my_data = [
{
name: "John",
age: 22
},
{
name: "Johnny",
age: 15
},
{
name: "Dave",
age: 27
}
]
I want to transform the data such that the substring "John" is replaced with "Ben" in each of the name properties so it looks like this:
[
{
name: "Ben",
age: 22
},
{
name: "Benny",
age: 15
},
{
name: "Dave",
age: 27
}
]
I want to do so in the proper functional way (I think is points-free but I am still learning), so I can reuse this in a pipeline, say first reducing by age and then doing the replace, or doing the replace first then doing a sort. How would I do this using the Ramda functions?
var fix_names = ???
var fixed_data = R.map( fix_names, my_data );
R.map(R.over(R.lensProp('name'), R.replace('John', 'Ben')))(my_data)
See R.over and R.lensProp.
There's no reason to prefer point-free functions. Readability is what really matters:
var myData = [ new Person("John", 22)
, new Person("Johnny", 15)
, new Person("Dave", 27)
];
var fixedData = myData.map(fixName);
alert(JSON.stringify(fixedData, null, 4));
function fixName(person) {
return Object.assign(new Person, person, {
name: person.name.replace(/John/g, "Ben")
});
}
function Person(name, age) {
this.name = name;
this.age = age;
}
Point-free functions are useful in very limited cases like eta conversion and function composition. Point-free functions should not be treated as the cornerstone of functional programming.

Wrap the function into another function

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

How to match mongo document against an array of rules one by one

I do a find and get a single document back. I also have a set of mongo rules. I need to match the document against this set of rules and if the document matches a rule, append the name of the rule to a rule-name subdocument.
Assume the document is this -
var randomGuy = { name: "Random Guy", age: 45, state: "assam", profession: "coder", ruleNames: [] };
I have that stored in a JavaScript variable. I also have a set of rules, converted to mongodb rules -
var rules = [
{'rule1': { name: /R*/i, age: { $gt: 40 } }},
{'rule2': { state: "karnataka" }},
{'rule3': { age: { $lt: 60 } }},
{'rule4': { $or: [ { profession: 'coder' }, { profession: 'programmer' } ] }}
];
I want to loop over the rules, match the randomGuy object against each and append the rule names to the randomGuy's ruleNames property. So the final randomGuy object looks like this -
var randomGuy = { name: "Random Guy", age: 45, state: "assam", profession: "coder", ruleNames: ['rule1', 'rule3', 'rule4'] };
I think i found the answer in sift.js - https://github.com/crcn/sift.js
All i will need to do is apply the rules as sift filters on the randomGuy object.
var randomGuys = [randomGuy];
var ruleName, ruleString;
rules.forEach(function(rule) {
ruleName = Object.keys(rule)[0];
ruleString = rule[ruleName];
if(sift(ruleString).test(randomGuy)) {
randomGuys.ruleNames.push(ruleName);
}
}

Categories

Resources