React Material UI Table Name Sorting seems little bit off - reactjs

Quick question about sorting the table. Anyone can explain why it's not sorting ASC or DESC example taken straight from Material UI Example: https://codesandbox.io/s/13r5l3qyz3,
Results:
Gargantua
Koala Den
aorro
or
aorro
Koala Den
Gargantua
Thanks

This behavior comes from the way the sort is implemented in this example. Here's the sort function (line 215 in the CodeSandbox you linked to):
const data =
order === "desc"
? this.state.data.sort((a, b) => (b[orderBy] < a[orderBy] ? -1 : 1))
: this.state.data.sort((a, b) => (a[orderBy] < b[orderBy] ? -1 : 1));
Assuming that you're ordering by name and order === "desc", that'll basically boil down to:
const data = this.state.data.sort((a, b) => (b.name < a.name ? -1 : 1));
So, the final order will be a result of comparisons that look like this:
"Koala Den" < "aorro"
"Gargantua" < "Koala Den"
But, JavaScript string comparison can have surprising results. Specifically, since the K in "Koala Den" is uppercase and the a in "aorro" is lowercase, this will be the result:
"Koala Den" < "aorro" // true
"Gargantua" < "Koala Den" // true
So, the sort is working as expected given the sorting method used. It's just not a case-insensitive string sort like you might expect.
You can check this by making "aorro" start with an uppercase A. Then the sort will have the expected results.
To fix this problem, the sort function would probably have to be re-implemented to work with all of the types that could be present in each column.

Converting the data to lower/upper case while comparing helps solve the problem. This also works for strings starting with numbers.
(i.e. for an array like ['Aeon', '3F Works', 'mBand', 'MAQ']
Here is the sample code:
const data =
order === "desc"
? this.state.data.sort((a, b) => (b[orderBy].toLowerCase() < a[orderBy].toLowerCase() ? -1 : 1))
: this.state.data.sort((a, b) => (a[orderBy].toLowerCase() < b[orderBy].toLowerCase() ? -1 : 1));

Related

Rewrite array recursive function with recursive output in middle

How can I rewrite this function with tail-recursion? I don't know if it's possible because the recursive call needs to be applied in the center of the array.
const weave = array =>
array.length <= 2
? array
: [ array[0], ...weave(array.slice(2)), array[1] ]
I wrote a function that finds the factor pairs of a number, where the pairs are of the form [ [1, x], [2, x/2], ... ] (assuming x is even, in this case). I want to flatten the array and sort it in O(n) time.
const getFactors = x => weave(getFactorPairs(x).flat())
Note: This is purely for my edification. Both the function above and array.flat().sort() are perfectly performant enough.
In true Stack-Overflow-as-rubber-duck fashion, the solution became obvious while writing my best attempt at an answer within the question, so obvious that I almost didn't bother posting.
const weave = (array, pre = [], post = []) =>
array.length <= 2
? [ ...pre, ...array, ...post ]
: weave(
array.slice(2),
[ ...pre, array[0] ],
[ array[1], ...post ]
)
Another option is to use continuation-passing style. This technique is commonly used to achieve a tail-call in recursive forms.
const identity = x =>
x
const weave = (a, then = identity) =>
a.length < 2
? then(a)
: weave(a.slice(2), r => then([a[0], ...r, a[1]]))
console.log(weave("abcdefg")) // [a,c,e,g,f,d,b]
weave("abcdefg", console.log) // [a,c,e,g,f,d,b]
The Trampoline technique mentioned in the comments can be implemented in various ways. This example is modelled after Clojure's loop/recur trampoline interface.
Here it is applied to your code. I traded array input for string so the output can be visualized more easily. weave processes a 1 million character string in less than 150ms.
function recur(...values) {
return Object.assign(values, {recur})
}
function loop(f) {
let result = f()
while (result?.recur === recur)
result = f(...result)
return result
}
const weave = (input = []) =>
loop((a = input, pre = "", post = "") =>
a.length < 2
? pre + a + post
: recur(a.slice(2), pre+a[0], a[1]+post))
console.time("weave 1M")
document.write(weave(`< > < >`.repeat(100000)))
console.timeEnd("weave 1M")

Using .Filter When the Filter Criteria are in Pre-existing String (TypeScript/Node.js)

I'm trying to process an array of JSON objects that have various common attributes, filtering each array entry on one or more of those attributes.
Normally, I'd just do it something like this:
let filteredResultsArray = originalArray.filter((obj) => {
return obj.attribute1 <= 3 && obj.attribute2 > 0 && obj.attribute3 === 10;
});
My problem is that the filter parameters (the part after "return" in the code above) are highly variable (and unpredictable) from run to run, so I can't hard-code them in the filter. I compute them on the fly and store the whole thing in a string in my code. For example, on one run it might be:
myAttributeString = "obj.attribute1 <= 3 && obj.attribute2 > 0 && obj.attribute3 === 10";
I've tried doing this:
let filteredResultsArray = originalArray.filter((obj) => {
return myAttributeString;
});
That's failing to filter anything. Apparently .filter() is not properly interpreting what I've stored in myAttributeString as filter criteria.
I have a sneaking suspicion that eval(myAttributeString) might be one way to pull this off, but unfortunately I'm working on a team where we've got tslint set to disallow the use of eval(), so that's not an option.
Anybody have an idea how I can get this to work?
When you "compute them on the fly", instead of creating a string, create a callback function that you can then pass to filter. For example, instead of
const myAttributeString = "obj.attribute1 <= 3 && obj.attribute2 > 0 && obj.attribute3 === 10";
do
const filterCallback = obj => obj.attribute1 <= 3 && obj.attribute2 > 0 && obj.attribute3 === 10
Then, later, when the appropriate time comes to .filter, simply pass that as the callback:
const filteredResultsArray = originalArray.filter(filterCallback);
If you can't pass functions around, another option would be to build an array of conditions, for example
[
{
prop: "attribute1",
constraint: "<=",
value: 3
},
{
prop: "attribute2",
constraint: ">",
value: 0
},
// ...
]
and then turn the object into the filter function needed.
****************************************UPDATE******************************
As I suspected, eval() did work, but since I can't use it in my delivered code, and thanks to CertainPerformance's suggestion (which put my thinking on the right track) as well as the Node.js documentation site (via a lucky Google search), I was able to find a workaround using the vm module:
import * as vm from "vm";
let filteredResultsArray = originalArray.filter(
vm.runInThisContext("(obj) => {
return " + myAttributeString + ";}"));
Case closed.

Angular 2 / Typescript - how to check an array of objects to see if a property has the same value?

This question does it in Javascript, but I would have thought in Typescript I could do some kind of map/filter operation to do the same thing.
I have an array of objects called Room. Each Room has a property called Width (which is actually a string, eg '4m', '5m', '6.5m').
I need to check the entire array to see if all the widths are the same.
Based on that question I have this, but I was wondering if TypeScript has something better:
let areWidthsTheSame = true;
this.qp.rooms.forEach(function(room, index, rooms) {
if (rooms[index] != rooms[index+1]) areWidthsTheSame = false;
});
Any ideas?
FYI the linked question has a comment that links to these performance tests, which are interesting in the context of this question:
This can be done in the following way:
const widthArr = rooms.map(r => r.width);
const isSameWidth = widthArr.length === 0 ? true :
widthArr.every(val => val === widthArr[0]);
We first convert the rooms array to an array of widths and then we check if all values in widths arrays are equal.

How to find a document with array size within a range in MongoDB?

Given the following document structure:
{
_id : Mongo ID,
data : [someData1, someData2, ..., someDataN]
}
I want to get all documents which data size is between a and b (strict positive integers, consider a < b is always true).
I first thought about using $size, but the Mongo doc states:
$size does not accept ranges of values. To select documents based on fields with different numbers of elements, create a counter field that you increment when you add elements to a field.
(Emphasis mine)
How to retrieve all the documents with a data array of length between a and b without using a counter?
Related but did not solve:
This answer which suggests using a counter
This question which asks how to query an internal length
and got many answers
This answer which suggests to use: { 'data.' + b : { $exists : true } } but I don't know how reliable it is and how it could apply to a range
This question about how to find documents with a minimal array length
You can use the $exists operator approach by including multiple terms in your query and building it up programmatically:
var startKey = 'data.' + (a-1);
var endKey = 'data.' + b;
var query = {};
query[startKey] = {$exists: true};
query[endKey] = {$exists: false};
db.test.find(query);
So for a=1 and b=3, for example, you end up with a query object that looks like:
{
"data.0" : {
"$exists" : true
},
"data.3" : {
"$exists" : false
}
}
The first part ensures there are at least a elements, and the second part ensures there are no more than b elements.

Sorting an arraycollection with a numeric value coming from 2 diffrent fields

Hi guys I need to sort an arraycollection. the problem is that my array is combined from two diffrent arrays, in one the age field is called "AGE", and in the other the age is called "MYAGE".
Now I need to sort the combined array according to age but I got two fields with diffrent names. I can go through the array and change all "MYAGE" to "AGE" but i was wondering if theres maybe a way to sort the array in its current status?
Thanks ahead
Say you have an ArrayCollection called myCollection. I hope the following will solve your request for that:
private function compareItems(a:Object, b:Object, fields:Array = null):int
{
var firstValue:Number = "AGE" in a ? a["AGE"] : a["MYAGE"];
var secondValue:Number = "AGE" in b ? b["AGE"] : b["MYAGE"];
if (firstValue == secondValue)
return 0;
return firstValue > secondValue ? 1 : -1;
}
…
var sort:ISort = new Sort();
sort.compareFunction = compareItems;
myCollection.sort = sort;
myCollection.refresh();

Resources