TypeORM conditional querying many to many relations - mysql

Let's say I've a colors table and an items table, with a #ManyToMany relation between these 2 tables (an item can have many colors and a color can have many items).
Using TypeORM, I would like to get all items that match only one or more provided colors.
If an item has an additional color that is not specified in the query, it should not be retrieved.
const matchingColors = ['green', 'blue'];
const query = await this.itemRepository
.createQueryBuilder('item')
.innerJoin('item.colors', 'color', 'color.name IN (:...matchingColors)', { matchingColors })
.getMany();
In my example above I would like to get all items that match the matchingColors array, so all items that have green, or blue, or green and blue color(s). So if an item has the blue and red colors, or only red (or no color), it should be excluded from the results.
Currently, I didn't find a proper way to do it. The query in my example above doesn't work correctly. My dream is to make it work using only 1 SQL query. Thanks for your help!

Try this:
const matchingColors = ['green', 'blue'];
const items = await this.itemRepository
.createQueryBuilder('item')
.innerJoinAndSelect(
'item.colors',
'color',
'"color"."name" IN (:...matchingColors)',
{ matchingColors }
)
.getMany();

Related

How to add legend for a bar chart with different colors in dc.js?

Below is the code snippet for a barchart with colored bars:
var Dim2 = ndx.dimension(function(d){return [d.SNo, d.something ]});
var Group2 = Dim2.group().reduceSum(function(d){ return d.someId; });
var someColors = d3.scale.ordinal().domain(["a1","a2","a3","a4","a5","a6","a7","a8"])
.range(["#2980B9","#00FFFF","#008000","#FFC300","#FF5733","#D1AEF1","#C0C0C0","#000000"]);
barChart2
.height(250)
.width(1000)
.brushOn(false)
.mouseZoomable(true)
.x(d3.scale.linear().domain([600,800]))
.elasticY(false)
.dimension(Dim2)
.group(Group2)
.keyAccessor(function(d){ return d.key[0]; })
.valueAccessor(function(d){return d.value; })
.colors(someColors)
.colorAccessor(function(d){return d.key[1]; });
How do I add a legend to this chart?
Using composite keys in crossfilter is really tricky, and I don't recommend it unless you really need it.
Crossfilter only understands scalars, so even though you can produce dimension and group keys which are arrays, and retrieve them correctly, crossfilter is going to coerce those arrays to strings, and that can cause trouble.
Here, what is happening is that Group2.all() iterates over your data in string order, so you get keys in the order
[1, "a1"], [10, "a3"], [11, "a4"], [12, "a5"], [2, "a3"], ...
Without changing the shape of your data, one way around this is to sort the data in your legendables function:
barChart2.legendables = function() {
return Group2.all().sort((a,b) => a.key[0] - b.key[0])
.map(function(kv) {
return {
chart: barChart2,
name: kv.key[1],
color: barChart2.colors()(kv.key[1]) }; }) };
An unrelated problem is that dc.js takes the X domain very literally, so even though [1,12] contains all the values, the last bar was not shown because the right side ends right at 12 and the bar is drawn between 12 and 13.
So:
.x(d3.scale.linear().domain([1,13]))
Now the legend matches the data!
Fork of your fiddle (also with dc.css).
EDIT: Of course, you want the legend items unique, too. You can define uniq like this:
function uniq(a, kf) {
var seen = [];
return a.filter(x => seen[kf(x)] ? false : (seen[kf(x)] = true));
}
Adding a step to legendables:
barChart2.legendables = function() {
var vals = uniq(Group2.all(), kv => kv.key[1]),
sorted = vals.sort((a,b) => a.key[1] > b.key[1] ? 1 : -1);
// or in X order: sorted = vals.sort((a,b) => a.key[0] - b.key[0]);
return sorted.map(function(kv) {
return {
chart: barChart2,
name: kv.key[1],
color: barChart2.colors()(kv.key[1]) }; }) };
Note that we're sorting by the string value of d.something which lands in key[1]. As shown in the comment, sorting by x order (d.SNo, key[0]) is possible too. I wouldn't recommend sorting by y since that's a reduceSum.
Result, sorted and uniq'd:
New fiddle.

python color entire pandas dataframe rows based on column values

I have a script that downloads a .csv and does some manipulation and then emails panda dataframes in a nice html format by using df.to_html.
I would like to enhance these tables by highlighting, or coloring, different rows based on their text value in a specific column.
I tried using pandas styler which appears to work however I can not convert that to html using to_html. I get a "AttributeError: 'str' object has no attribute 'to_html"
Is there a another way to do this?
As an example lets say my DF looks like the following and I want to highlight all rows for each manufacturer. i.e Use three different colors for Ford, Chevy, and Dodge:
Year Color Manufacturer
2011 Red Ford
2010 Yellow Ford
2000 Blue Chevy
1983 Orange Dodge
I noticed I can pass formatters into to_html but it appears that it cannot do what I am trying to accomplish by coloring? I would like to be able to do something like:
def colorred():
return ['background-color: red']
def color_row(value):
if value is "Ford":
result = colorred()
return result
df1.to_html("test.html", escape=False, formatters={"Manufacturer": color_row})
Surprised this has never been answered as looking back at it I do not believe this is even possible with to_html formatters. After revisiting this several times I have found a very nice solution I am happy with. I have not seen anything close to this online so I hope this helps someone else.
d = {'Year' : [2011, 2010, 2000, 1983],
'Color' : ['Red', 'Yellow', 'Blue', 'Orange'],
'Manufacturer' : ['Ford', 'Ford', 'Chevy', 'Dodge']}
df =pd.DataFrame(d)
print (df)
def color_rows(s):
df = s.copy()
#Key:Value dictionary of Column Name:Color
color_map = {}
#Unqiue Column values
manufacturers = df['Manufacturer'].unique()
colors_to_use = ['background-color: #ABB2B9', 'background-color: #EDBB99', 'background-color: #ABEBC6',
'background-color: #AED6F1']
#Loop over our column values and associate one color to each
for manufacturer in manufacturers:
color_map[manufacturer] = colors_to_use[0]
colors_to_use.pop(0)
for index, row in df.iterrows():
if row['Manufacturer'] in manufacturers:
manufacturer = row['Manufacturer']
#Get the color to use based on this rows Manufacturers value
my_color = color_map[manufacturer]
#Update the row using loc
df.loc[index,:] = my_color
else:
df.loc[index,:] = 'background-color: '
return df
df.style.apply(color_rows, axis=None)
Output:
Pandas row coloring
Since I do not have the cred to embed images here is how I email it. I convert it to html with the following.
styled = df.style.apply(color_rows, axis=None).set_table_styles(
[{'selector': '.row_heading',
'props': [('display', 'none')]},
{'selector': '.blank.level0',
'props': [('display', 'none')]}])
html = (styled.render())

How to split Map into two maps based on a condition

I'm relatively new to React using Immutable.js.
Let's say I have a (Ordered) Map of 30 items, in which some have the color green and some the color red.
Now I want to split this Map into two Maps, one containing the first five green items and the other containing the rest (rest of the green items and red items).
If I had an array, I would just define two result-arrays, iterate through my src-array and put the items in their according result-array. If I did that with immutable.js, I would need to create a new Map every time something changes. Is that still the way to go, or are there faster / more elegant ways to achieve that?
Thanks in advance!
If I get the question right, most elegant way is to use Map.filter
const { Map } = Immutable;
const sourceMap = new Map({
key1: { color: "red" },
key2: { color: "green" },
});
const filterMap = c => sourceMap.filter(({ color }) => color === c);
const greenMap = filterMap("green");
const redMap = filterMap("red");
<script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/3.8.1/immutable.min.js"></script>

Combining two items (recipe-like)

I'd like a user to be able to combine two items and if compatible will yield a new item. In this example, the item IDs will be saved as Strings.
I was wondering what the most efficient way to do this would be, while making sure that swapped order will always yield the same result, so the user could input the order:
item X + item Y = item Z
item Y + item X = item Z
I've tried using Dictionaries and Objects, but I just haven't been able to get anything to work. I've also tried some various libraries that include HashMap/HashSet but nothing is working. here's some pseduo-code:
itemRecipe1:HashSet = new HashSet();
itemRecipe1.add("2");//Add item with ID of 2
itemRecipe1.add("3");//Add item with ID of 3
inputRecipe:HashSet = new HashSet();
inputRecipe.add("3");//Add item with ID of 3 (swapped)
inputRecipe.add("2");//Add item with ID of 2 (swapped)
recipeList:HashMap = new HashMap();
receipeList.put(itemRecipe1, "11");//Recipe has been added, the result of the recipe should be item 11
//This should output as TRUE since the composition of itemRecipe1 and inputRecipe are the same, despite a different input order.
trace(receipeList.containsKey(inputRecipe));
If anyone has a solution for this issue, please elt me know as I am willing to implement any design I can get working. I just don't see how a Dictionary could work as the key order matters.
So you're trying to associate two or more objects with each other. The first thing you need is some primitive data you can use to represent each item uniquely, typically an ID. This should give you something like the following to begin with:
class Item {
public var _id:int;
public function Item(id:int) {
_id = id;
}
public function get id():int { return _id; }
}
Now you need some piece of data that establishes a relationship between multiple Items using this ID. That could be as simple as the following, with a little extra functionality thrown in to see if an input list of these IDs matches the relationship:
class ItemRelationship {
private var _items:Vector.<Item>;
public function ItemRelationship(items:Vector.<Item>) {
_items = items;
}
public function matches(ids:Vector.<int>):Boolean {
if (_items.length !== ids.length) {
return false;
}
for each (var item:Item in _items) {
var found:Boolean = false;
for each (var id:int in ids) {
if (item.id === id) {
found = true;
break;
}
}
if (!found) return false;
}
return true;
}
public function get items():Vector.<Item> { return _items; }
}
This lets us do something like this, assuming we have a bunch of items (item1, item2, ...) with IDs.
var rel:ItemRelationship = new ItemRelationship(new <Item>[item1, item2]);
And then:
trace(rel.matches(new <int>[1,2])); // true
trace(rel.matches(new <int>[2,1])); // true
trace(rel.matches(new <int>[3,4])); // false
Now all we need is something that stores all of these relationships and lets us fetch one based on a list of input IDs:
class RelationshipCollection {
private var _relationships:Vector.<ItemRelationship>;
public function RelationshipCollection(relationships:Vector.<ItemRelationship>) {
_relationships = relationships;
}
public function find(ids:Vector.<int>):ItemRelationship {
for each(var relationship:ItemRelationship in _relationships) {
if (relationship.matches(ids)) return relationship;
}
return null;
}
}
Put a load of relationships in there:
var collection:RelationshipCollection = new RelationshipCollection(new <ItemRelationship>[
new ItemRelationship(new <Item>[item1, item4]),
new ItemRelationship(new <Item>[item2, item3])
]);
And give it a whirl:
trace(collection.find(new <int>[1, 3])); // null (no match)
trace(collection.find(new <int>[1, 4])); // works
trace(collection.find(new <int>[3, 2])); // works
trace(collection.find(new <int>[2, 3])); // works
Of course for the sake of readability you can rename each class to something more appropriate for its application e.g. Item => Potion, ItemRelationship => Recipe, RelationshipCollection => RecipeBook.
so the user could input the order
The first step is to limit the possible input. If you allow any type of input, you have to parse that input and things get complicated very quickly.
Create an input method that only allows the user to put two items together, say for example via drag and drop of the items to only 2 slots.
I just don't see how a Dictionary could work as the key order matters.
The important part is to design the keys well.
As #George Profenza pointed out in the comments, you could change your IDs to a different format. Instead of having 1, 2, 3, ... n you could use 1, 2, 4, ... 2^n. The advantage is that you can combine any two IDs uniquely via bitwise or operator (|). In the following example, two such IDs are combined (binary notation):
00001
| 10000
--------
10001
As you can see, each ID occupies a separate position in binary: the 1st position and the 5th. Combining both via or operator means that now both 1st and 5th position are 1. The order doesn't matter. If you use such IDs in the form of powers of 2 you can combine them regardless of the order to form pairs, which can then be used as keys to a dictionary.
Another solution is to simply sort the pair of IDs.
The combination 3-2 becomes 2-3 and the combination 2-3 stays 2-3. Both 2-3 and 3-2 lead to the same result.
You can then build your data structure accordingly, that is: the outer data structure is for the lower ID number and the nested, inner one is for the bigger ID number. Here's some pseudo code with generic objects:
var map:Object = {};
map["2"] = {"3":"combination 2-3"};
To access that, you'd do something like:
trace(map[Math.min(ID1, ID2)][Math.max(ID1, ID2)])
There's also the brute force way of doing it by storing both possible combinations in the data structure. The code for that could roughly look like that:
var map:Object = {};
map["2"] = {"3":"combination 2-3"};
map["3"] = {"2":"combination 2-3"};
Now both
trace(map[ID1][ID2]);
and
trace(map[ID2][ID1]);
Should yield the same result.

How to count for specific string and color?

I've been looking into how to count cells with the countif function, and how to count cells that are colored using scripts and custom functions (like this thing: http://pastebin.com/4Yr095hV), but how would i count cells with a specific string AND color?
Example, I want to count every cell containing the word "one" that has a fill color of white.
EDIT: I was told to add what i had so far, but I am not sure what was meant by that. For counting cells with a specific string I used:
=COUNTIF(A1:A247,"string")
and for counting cells that are colored i used this what was on this page: https://webapps.stackexchange.com/questions/23881/google-spreadsheet-calculating-shaded-cells
but i still don't know how to combine these two TOGETHER.
EDIT: For those looking for this answer, I've found a way to utilize the script Tom posted, and adjusted a line within it.
For Tom's script to work with "wildcards", i used something called .indexOf to always look for any cells containing the string (effectively treating it as if there is always a star before and after the string). On line 32 of his script, I altered it to this:
.map (function(e,i,a) { if (e.toString().toUpperCase().indexOf(this.toString().toUpperCase()) >= 0){ return 1 } else { return 0 } },str))
So now whenever I want to look for a White cell containing the string "Apple1", it will count it regardless of if it's written as "OrangeApple1B" or whatever. And the casing doesn't matter since it seems like this script always converts the given string to Upper Case anyways.
I am still trying to find out how to incorporate this on a totally different spreadsheet though (using something like IMPORTRANGE to count cells on a TOTALLY DIFFERENT SHEET using this script)...
function countIfStringAndColor(r, str, color) {
var COLORS = {
"BLACK":"#000000",
"DARK GRAY 4":"#434343",
"DARK GRAY 3":"#666666",
"DARK GRAY 2":"#999999",
"DARK GRAY 1":"#B7B7B7",
"GRAY":"#CCCCCC"
};
var range = SpreadsheetApp
.getActive()
.getActiveSheet()
.getRange(r.toString());
color = color.indexOf("#") == 0 ? color : COLORS[color.toString().toUpperCase()];
return range
.getBackgrounds()
.reduce(function(a,b) { return a.concat(b) })
.map (function(e,i,a) { return e.toString().toUpperCase() === this.toString().toUpperCase(); },color)
.map(function(e,i,a) { return [e, this[i]] },
range
.getValues()
.reduce(function(a,b) { return a.concat(b) })
.map (function(e,i,a) { return e.toString().toUpperCase() === this.toString().toUpperCase() },str))
.filter(function(e,i,a) {return a[i][0] && a[i][1] })
.length;
}
METHOD OF OPERATION
The function takes three arguments: Range (String), String, String
The associative array 'COLORS' is supplied to convert the common names of colors to hex format. There are about 90 more colors in the list that I didn't supply for space reasons. I can get you the full list if you would like.
Grabbing the Range.
Checks to see if color is already in hex format. If not it tries to find a common name key in COLORS and return the hex value. From here out everything is toString() and toUpperCase() to help prevent errors.
The code from here out is one chain of array manipulation that will produce the solution for the function to return.
Grab the needed background colors.
.reduce, coupled with .concat (both Array Methods), is used to flatten the background color array. It changes it from a rectangular array of arrays to a one dimensional list.
.map goes through each element of the array and applies the given function. In this case we are seeing if the array element (e) is the same as the color supplied. Take note of how 'color' is called outside the closing curly bracket. It is the 'thisArg', and the 'this' inside the function is an image of it. The array is now reduced to a series of true/false elements.
This map is used to combine the two arrays, 'color' and 'str'. The indented part right below is the same steps we used to get 'color' to a series of true/false elements, but now applied to 'str'. All those operations are performed while 'str' is being called as the thisArg for the current map function. The map function then returns a single array of the form [color,str] which is made up of many elements of [true,false] [true,true] [false,false] pairs.
We are only interested in the solutions where both 'color' and 'str' are true, so we can use .filter to remove all the other elements, leaving use with an array of only [true, true] pairs.
Each [true, true] pair is a unique solution to the equation. We can just grab the length of the array to see how many solutions we have found! This is the value that is passed to the return at the beginning.