I am putting in a temporary fix to code in which we want to validate attributes on an item. These are "swatchImageUrl" and "swatchVariantAttribute". If either one of these is provided, the other must be provided. Where I will check this is on a dictionary of the values. So what I have in place is the following:
if((transformedValues.Any(t => t.Key.Equals("swatchImageUrl")) &&
!transformedValues.Any(t => t.Key.Equals("swatchVariantAttribute"))) ||
(transformedValues.Any(t => t.Key.Equals("swatchVariantAttribute")) &&
!transformedValues.Any(t => t.Key.Equals("swatchImageUrl"))))
{
// throw an error here
}
This feels clunky and possibly inefficient (transformedValues will possibly be a very large list and my understanding is .Any() will end up enumerating the whole list if there are none) but I cannot think of a nicer way of doing this. 'transformedItems' is an IList of string key value pairs (so I can't use .ContainsKey etc.)
Is there some nice neater way of doing this that I am missing? Any insight is much appreciated.
Just in case anyone else is having similar brainfreeze. The obvious way to do this in a better way is as ASh pointed out;
if(transformedValues.Any(t => t.Key.Equals("swatchImageUrl")) != transformedValues.Any(t => t.Key.Equals("swatchVariantAttribute")))
{ /*...*/ }
Related
I am attempting to do some string manipulation in rjxs, and while I can accomplish it with the built in behaviors on the string class and the array class in Javascript, I'm wanting to use this as an exercise to learn even more about rxjs and understand a fluent code-flow better.
To that end, it's imperative to me that I discover a way to do it that can fit in a fluent solution, instead of a series of variable assignments like I see in most rxjs examples.
Essentially, here's the situation; I've got a string of text;
const example = `
key || value
key || value
key || value
value
value
value
key || key[key1] = value | key[key2] = value
key || value
`;
The first thing that I need to do is use string.split('\n') to create an array of strings, so that I can through each line and perform further operation.
example.string.split('\n') does give the desired results, but trying to send this into rxjs begins to get rather mixed yield. With the pipe method, I know that I send the results into rxjs as an Observable, but I'm having a really troubling time grasping how to truly treat it from there without excessive nesting into the map operator.
For example, if I do ...
of(example.string.split('\n')).pipe(
map(results => results.toString().split('||')),
map(results => ... ),
...
).subscribe();
I can start to get a semblance of what I'm looking for, but what I'd really like to do is ...
of(example).pipe(
split('\n'),
split('||'),
concatMap(results => ...)
).subscribe();
Reading the documentation on lettable operators, seen here, it looks like this should be a pretty easy thing to create. In theory, it should look like this in my mind;
const split = (separator: string) => <T>(source: Observable<T>) =>
new Observable(observer => {
source.subscribe({
next(x) { observer.next(x.toString().split(separator)); },
error(err) { observer.error(err); },
complete() { observer.complete(); }
})
});
So that should make the whole code obvious enough;
of(example).pipe(
split('\n')
).subscribe(result => console.log(`[n]::${result}`));
But this doesn't give me what I really expect. I expected to get an array of the lines, but if I output it, I get ...
[n]::, key || value, key || value, key || value, ,
value, value, , value, key || key[key1] = value |
key[key2] = value, key || value,
I'm really unclear what I'm doing wrong, here. Since it's hard to demonstrate rxjs in most of the code playgrounds like plunkr or jsfiddle, at least to my knowledge, I've prepared a playground environment to demonstrate my work on stackblitz, if it helps.
You'll find all of the pertinent code in the playground/index.ts file. I've done the best I can to abstract away the need to have any knowledge of angular, as I've painstakingly earmarked the sections that should be left alone to make it continue showing output on the right side. If you do not know angular, but can help with rxjs, you should be able to work without ever disturbing that setup.
STACKBLITZ PLAYGROUND
Your code is working fine, just the es6 template string ${} flattened your array into a string. If you console.dir or log the result, you will see a correct array retrieved.
For a field inside a deeply nested table, for example, text.title.1.font. Even if you use
if text.title.1.font then ... end
it would result in an error like "attempt to index global 'text' (a nil value)" if any level of the table does not actually exists. Of course one may tried to check for the existence of each level of the table, but it seems rather cumbersome. I am wondering is there a safe and prettier way to handle this, such that when referencing such an object, nil would be the value instead of triggering an error?
The way to do this that doesn't invite lots of bugs is to explicitly tell Lua which fields of which tables should be tables by default. You can do this with metatables. The following is an example, but it should really be customized according to how you want your tables to be structured.
-- This metatable is intended to catch bugs by keeping default tables empty.
local default_mt = {
__newindex =
function()
error(
'This is a default table. You have to make nested tables the old-fashioned way.')
end
}
local number_mt = {
__index =
function(self, key)
if type(key) == 'number' then
return setmetatable({}, default_mt)
end
end
}
local default_number_mt = {
__index = number_mt.__index,
__newindex = default_mt.__newindex
}
local title_mt = {__index = {title = setmetatable({}, default_number_mt)}}
local text = setmetatable({}, title_mt)
print(text.title[1].font)
Egor's suggestion debug.setmetatable(nil, {__index = function()end}) is the easiest to apply. Keep in mind that it's not lexically scoped, so, once it's on, it will be "on" until turned off, which may have unintended consequences in other parts of your code. See this thread for the discussion and some alternatives.
Also note that text.title.1.font should probably be text.title[1].font or text.title['1'].font (and these two are not the same).
Another, a bit more verbose, but still usable alternative is:
if (((text or {}).title or {})[1] or {}).font then ... end
For example, say I want to do this:
{
:mytime => times[:mytime]
}
There is a bit of repetition here, is it possible to just do something like { :mytime => times[$_key] } (made up syntax)?
The short answer is: no, there's no syntax that does anything like that. Without more context about what you're actually trying to do, there isn't really a good longer answer. Still, I'll try to be a little more useful. If you're really worried about repetition, you could do something like:
h = {}
[:mytime, :yourtime].each do |k|
h[k] = times[k]
end
For only a few keys, I can't see that being worth it, personally.
How could I go about grouping the following?
people.GroupBy(p=>p.Addresses.GetFirstOrDefault().State);
without it failing on people who don't have an Address?
can it be done in one statement?
if not, do I have to first get a Distinct() list of all the various addresses members? How? (actually -- even if a is possible -- would be great to also learn how to do b :-) )
I havn't seen it, but is there something equivalent to a GetFirstOrNew() that can be used to instantiate and return a non-null?
Thank you very much!
It can be done in one statement, yes:
// Set up whatever you need
Address dummyAddress = new Address { State = "" };
people.GroupBy(p => (p.Addresses.GetFirstOrDefault() ?? dummyAddress).State);
Alternatively, you might want to write a helper method:
public string GetState(Address address)
{
return address == null ? null : address.State;
}
Then you can use:
people.GroupBy(p => GetState(p.Addresses.GetFirstOrDefault()));
I'm sure this is a trivial question but I couldn't find a good example. Suppose all you want to do is change one attribute for all objects in a list. I'd like to say something like:
List<SomeType> list = ...;
list.Select(x => x { x.Name = "Foo" } );
Notice the absence of the "new" keyword. I don't want to recreate objects that already exist, just execute one line of code (in this case a simple assignment) on every element of the list.
Is this possible in linq in some elegant way?
Thanks in advance!
Very easy. MSDN ForEach its actually just a method of the List class, but it allows for you to use Lambda expressions.
list.Foreach(x => x.Name = "Foo" );
It doesn't really fall under Linq if you want to mutate the collection. See Eric Lippert's post to see why.
Try List<T>.ForEach()