Lodash deep pick - javascript

Hi I'm getting data from a soap service and transforming the xml to json and getting the value I need like this:
console.log(result['soap:Envelope']['soap:Body']['ns2:getFichaGeneralResponse']['return']['instituciones']['datosPrincipales']['registros'][1].valor)
is there any way to do something like this?
console.log(_.pick(result, 'registros'))
So I can get an object with the information I need?

If I understand you correctly, you have a complicated object and you want to pick some props by the specified path?
For example, you want to get { d: 'foo', e: 'bar' } by path a.b.c from the object below:
var object = {
a: {
b: {
c: {
d: 'foo',
e: 'bar',
f: 'baz'
}
}
},
g: {
h: 1
}
};
function pickPropsByPath(object, path, arrayOfPropsNames) {
return _.pick(_.get(object, path), arrayOfPropsNames);
}
console.log(pickPropsByPath(object, 'a.b.c', ['d', 'e'])) // => { d: 'foo', e: 'bar' }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>

Related

Is it possible to have optionally nested key-value pairs in TypeScript?

I would like to have an object like this:
const foo = { ... }; // how?
console.log(foo.a); // '1'
console.log(foo.a.bar); // { 'prop1': 1, 'prop2': 2 }
console.log(foo.b); // '2'
console.log(foo.b.bar); // { 'prop1': 1, 'prop2': 2 }
So, the default value of foo.a is described in the comment after the statement with the value '1'. But I would also like to go deeper into the object, to access a deeper property bar, of which a is the parent.
Something to demonstrate the desired structure:
const foo = {
a: '1',
[a.bar]: { prop1: 1, prop2: 2 },
b: '2',
[b.bar]: { prop1: 1, prop2: 2 }
}
Is this possible with TypeScript without using a function to call it? So something like this would not be a good solution for this case:
foo.a();
foo.a().bar();
Don't use this pattern.
Another version of what you seem to be asking is "Can I extend primitive types with additional properties"?
Is this possible?
Yes (sort of), but it's a code smell and I've never seen a compelling case for it. Here are two reasons why you shouldn't do it:
Anyone reviewing the code (even your future self) won't be expecting extra properties on these types, which will make it harder to read and understand.
Especially if the property names are going to be dynamic, there's a higher likelihood of overwriting/shadowing existing, standard property names on the object form of the primitive, which will almost certainly cause bugs.
With that out of the way — and, again, don't use this (you've been warned) — here's how you can do it:
References:
Object.assign()
String primitives and String objects
TS Playground
const bar = { prop1: 1, prop2: 2 };
const foo = {
a: Object.assign('one', { bar }),
b: Object.assign('two', { bar }),
};
/* The type of "foo" is:
{
a: "one" & {
bar: {
prop1: number;
prop2: number;
};
};
b: "two" & {
bar: {
prop1: number;
prop2: number;
};
};
}
*/
console.clear();
// This is a `String` (object), not a `string` (primitive):
console.log(foo.a); // String "one" { bar: { prop1: 1, prop2: 2 } }
// You can stringify it using any of these methods:
console.log(String(foo.a)); // "one"
console.log(foo.a.toString()); // "one"
console.log(`${foo.a}`); // "one"
console.log(foo.a.toUpperCase()); // "ONE"
console.log(foo.a.bar); // { prop1: 1, prop2: 2 }
console.log(foo.b); // String "two" { bar: { prop1: 1, prop2: 2 } }
console.log(String(foo.b)); // "two"
console.log(foo.b.toUpperCase()); // "TWO"
console.log(foo.b.bar); // { prop1: 1, prop2: 2 }
It would be better to devise a system for yourself of storing the value on a property on an object, (for example, as David suggested, using the value property).
Well, there's a possible solution but it's maybe not exactly what you want.
See when creating an object (for ex. a class), you have to possibility to give it a toString method. This way when the object is concatenated into a string. It executes your method instead. Here's an example:
const foo = {
a: {
prop1: 123,
prop2: 456,
toString(){
return "1" }
},
b: {
prop1: 123,
prop2: 456,
toString(){
return "1"
}
}
}
// The problem is that if you want to get the `"1"` of an object. You have to convert it into a string like for example :
console.log(`${foo.a}`)
console.log(foo.a.toString())
console.log("" + foo.a)
// And of course you can still access every possible property
console.log(foo.a.prop1)
console.log(foo.b.prop2)
console.log(foo.b.prop1 + ' - ' + foo.b)

Object.assign(...as) changes input parameter

Object.assign(...as) appears to change the input parameter. Example:
const as = [{a:1}, {b:2}, {c:3}];
const aObj = Object.assign(...as);
I deconstruct an array of object literals as parameter of the assign function.
I omitted console.log statements. Here's the stdout from node 13.7:
as before assign: [ { a: 1 }, { b: 2 }, { c: 3 } ]
aObj: { a: 1, b: 2, c: 3 }
as after assign: [ { a: 1, b: 2, c: 3 }, { b: 2 }, { c: 3 } ]
The reader may notice that as first element has been changed in an entire.
Changing a new array bs elements to an immutable object (using freeze)
const bs = [{a:1}, {b:2}, {c:3}];
[0, 1, 2].map(k => Object.freeze(bs[k]));
const bObj = Object.assign(...bs);
leads to an error:
TypeError: Cannot add property b, object is not extensible
at Function.assign (<anonymous>)
Which indicates the argument is indeed being changed.
What really confounds me is that even binding my array, cs, by currying it to a function (I think you call this a closure in JS)
const cs = [{a:1}, {b:2}, {c:3}];
const f = (xs) => Object.assign(...xs);
const g = () => f(cs);
const cObj = g();
returns:
cs before assign: [ { a: 1 }, { b: 2 }, { c: 3 } ]
cObj: { a: 1, b: 2, c: 3 }
cs after assign: [ { a: 1, b: 2, c: 3 }, { b: 2 }, { c: 3 } ]
What went wrong here? And how may one safely use Object.assign without wrecking its first argument?
Object.assign is not a pure function, it writes over its first argument target.
Here is its entry on MDN:
Object.assign(target, ...sources)
Parameters
target
The target object — what to apply the sources’ properties to, which is returned after it is modified.
sources
The source object(s) — objects containing the properties you want to apply.
Return value
The target object.
The key phrase is "[the target] is returned after it is modified". To avoid this, pass an empty object literal {} as first argument:
const aObj = Object.assign({}, ...as);

How to use include in Chai?

I'm trying to figure how can I pass an object and just check if the object is in the response using either that.includes or to.include from chai
I wrote a simple fiddle to check my problem:
https://jsfiddle.net/balexandre/4Loupnjk/2/
https://jsfiddle.net/balexandre/4Loupnjk/5/ with .deep flag
var e = {
"results": {
"total_rejected_recipients": 0,
"total_accepted_recipients": 1,
"id":"102618457586465882"
}
};
from my understanding, the e object should actually have the smaller object included... or am I missing something?
expect(e).to.include({
"results": {
"total_rejected_recipients": 0,
"total_accepted_recipients": 1
}
});
but I get the error:
assertionError: expected { Object (results) } to have property 'results' of { Object (total_rejected_recipients, total_accepted_recipients) }, but got { Object (total_rejected_recipients, total_accepted_recipients, ...) }
at Context.<anonymous> (:73:18)
First time on this framework though, might be the issue :)
First of all, you should use the deep.include assertion, as you have a deep object in there.
Anyways, it looks like this is a bug. The github ticket where this was implemented is located here and the relevant commit here.
The test coverage for this assertion is here:
expect({foo: obj1, bar: obj2}).to.deep.include({foo: {a: 1}});
expect({foo: obj1, bar: obj2}).to.deep.include({foo: {a: 1}, bar: {b: 2}});
expect({foo: obj1, bar: obj2}).to.not.deep.include({foo: {a: 9}});
expect({foo: obj1, bar: obj2}).to.not.deep.include({foo: {z: 1}});
expect({foo: obj1, bar: obj2}).to.not.deep.include({baz: {a: 1}});
expect({foo: obj1, bar: obj2}).to.not.deep.include({foo: {a: 1}, bar: {b: 9}});
However, it breaks in the following scenario:
expect({ foo: obj1, bar: obj2 }).to.deep.include({ foo: { a: 1 }, bar: { } });
Better open the issue in chai repository, and temporary use the chai-subset package.

Lodash reject get return object

var obj = {a: [], b: [1,2], c: [], d: [1]};
How do I get a non-empty array of objects like the following:
{b: [1,2], d: [1]}
You can do what you are after, using pickBy().
var result = _.pickBy(obj, function(val){
return val.length > 0;
});
Fiddle here: https://jsfiddle.net/W4QfJ/3160/
Note: Unlike filter() and reject(), this returns an object, keeping your original structure (rather than an array).
Another way to do this: _.omitBy(obj, _.isEmpty);
_.filter() is what you're looking for:
var obj = {a: [], b: [1,2], c: [], d: [1]};
console.log(_.filter(obj, function(o){ return o.length; }))
If you want to use _.reject() like in your title, you can do something like this:
_.reject({a: [], b: [1,2], c: [], d: [1]},function(o){
return o.length == 0
});
Right now, Lodash has a method called _.omit that does exactly what you need:
> const object = {a: 1, b: 2, c: 3, d: 4}
undefined
> _.omit(object, ['a', 'c'])
{ b: 2, d: 4 }

Parse.com retrieving value of nested object

Lets say i have object like this:
var a = {
b: {
c: 1,
d: 2
}
}
And i have saved this object in Parse backend. There are 100 objects, but with different c and d values. Can i do search searching only objects which contains d:2. Or the only way is to query all objects and then use for loop which will search for d:2?
Dont read this!
Writting this line just to get posted, because system does not allow to post me question, dont know why
Thank you
Update
Maybe i am not clear enough, I am using parse.com you can retrieve objects by using this line:
var GameScore = Parse.Object.extend("GameScore");
var query = new Parse.Query(GameScore);
query.equalTo("playerName", "Dan Stemkoski");
query.find();
If anybody knows if you can retrieve objects with specific values in nested objects, that would be great.
You can do something like this
var a = {
b0: {
c: 1,
d: 2
},
b1: {
c: 5,
d: 3
},
b2: {
c: 1,
d: 4
},
b3: {
c: 2,
d: 2
},
b4: {
c: 1,
d: 4
},
b5: {
c: 7,
d: 2
},
},
d2s = Object.keys(a).filter(e => a[e].d == 2).map(e => a[e]);
document.write("<pre>" + JSON.stringify(d2s,null,2) + "</pre>");

Categories