I have a problem when I try to run my unit test of my Angular component and the console shows this message. This occurs when the component should be created.
result.sort is not a function
The message in console
ResultComponent › should create
TypeError: result.sort is not a function
45 | sortResult(result: any[]) {
46 | console.log(result)
> 47 | let sortedResult = result.sort((a, b) =>
| ^
48 | a.name.localeCompare(b.name)
49 | );
50 | return sortedResult;
ResultComponent code is here:
sortResult(result: any[]) {
console.log(result)
let sortedResult = result.sort((a, b) =>
a.name.localeCompare(b.name)
);
return sortedResult;
}
I tried to search a solution to this problem but every answer refers to Objects that have not been converted to Arrays. I don't know if this is the same problem.
There is a possibility that you are passing in something other than an array, most likely an object.
Have you checked if it is actually an array?
console.log(Array.isArray(result));
If It is not an array you can try converting it using the .from() method
Hope this helps
Related
I have this code:
totalPrice = Object.entries(
buyTicketData.pricingAndInvoicingType ==
PricingAndInvoicingType.invoiceItems
? buyTicketData.invoiceItems
: buyTicketData.pricingOptions[startPaymentIn?.pricingOptionId]
.invoiceItems
).reduce((newObj, [key, val]) => {
(cumulativValue, ii) => {
const ret =
cumulativValue +
(val.quantity ?? startPaymentIn?.invoiceItems[key] ?? 0) *
parseInt(val.unitPrice);
return ret;
};
}, 0);
In my understanding preiousValue is a cumulativ value, and it could be different in type with currentValue. In my case previousValue is a number while current value is an array with a keyandvalue` fields.
But it does not work, something work, maybe whole approach is wrong. What do you think?
./pages/hu/buyTicket/[eventId].tsx:75:5
Type error: Type '[string, InvoiceItemData]' is not assignable to type 'string'.
73 | startPaymentIn?.pricingOptionId)
74 | ) {
> 75 | totalPrice = Object.entries(
| ^
76 | buyTicketData.pricingAndInvoicingType ==
77 | PricingAndInvoicingType.invoiceItems
78 | ? buyTicketData.invoiceItems
error Command failed with exit code 1.
I think you're not returning anything inside of reduce function. Reduce function needs return value, and that return value is passed to previousValue in next iteration. So it is accumulative in this point because it manipulates and returns data, use as next iteration's previousValue. So try to return something inside of it. Also, I think you're using typescript, so I recommend you to insert some type defs at reduce function.
Object.entries turns an object into a list of key-value pairs.
For example:
let invoiceItems = {
'key-1': 100,
'key-2': 200,
};
Object.entries(invoiceItems); // [['key-1', 100], ['key-2', 200]]
If you now want to reduce this list, the first two arguments of the reduce callback function will be:
an accumulator (if you want to calculate the sum of something, this will be a number)
each item of the list, in my example they are of type [string, number]
To build on my example, I could add all the values together:
let totalPrice = Object.entries(invoiceItems).reduce(
(cumulativeValue, [key, val]) => {
// in my case, key is a string and val is of type number
return cumulativeValue + val;
},
0 // the initial value
);
// totalPrice === 300
I hope this somewhat explains how the types come to be. To answer your question, I think your reducer is a bit messed up as there are 2 functions. To fix it, this should work:
.reduce(
(cumulativeValue, [key, val]) => {
const ret =
cumulativeValue +
(val.quantity ?? startPaymentIn?.invoiceItems[key] ?? 0) *
parseInt(val.unitPrice);
return ret;
},
0
);
Finally I figured out rather than reduce I need a simple for loop.
I've come across the following code which is a filter callback function on an array. I'm confused trying to understand what this function is doing and am trying to break it down into smaller bits to understand but I can't seem to get my head around it.
I'm trying to learn TypeScript and know some JS but the syntax is confusing for me.
Could someone please explain how this function would operate on an arrays inputs please? a sort of walk through of the syntax in this exampled would be helpful. Thanks.
const check = (Boolean as any) as <T>(
a: T | false | undefined | null | ''
) => a is T;
Ok, this function is kinda... crazy. Let's breakdown this code a bit.
First, take the following idiom as an example:
const something = ('' as any) as boolean
// something's type is boolean for Typescript, but its value is actually a string!
the (X as any) as Y idiom is a hack that allows you to tell typescript that something is actually something else. You're taking full control of the type system, and Typescript won't be able to infer the underlying value - and much less catch it. It can lead to bugs, such as the above example where I tell TS that an empty string is a boolean.
Now, consider the next idiom:
const isTruthy: <T>( x: T | false | undefined | null | '' ) => x is T
// not yet implemented
This declaration tells typescript that "this is a function that takes any type of parameter, and if it returns true it means the parameter is definitely not falsy". This is done by using Type guards through the return type "x is T". This is useful because it'll allow you to take a possible-falsy value and verify that it is, in fact, not falsy:
const a: any[] | null | undefined = [];
if(isTruthy(a)) {
// a is any[]
}
One very simple way to implement isTruthy is by using the Boolean function:
type Falsy = null | undefined | false | 0 | ''
function isTruthy<T>(value: T | Falsy): value is T {
return Boolean(value);
}
So, whats the point?
let's take a look at your example once again:
const check = (Boolean as any) as <T>(
a: T | false | undefined | null | ''
) => a is T;
Boolean in this code is a value. Specifically, it's the Boolean Function.
it is cast to any and then cast to <T>( x: T | false | undefined | null | '' ) => x is T
So, all this code does is declare an alias for the Boolean function and tell TS that it is a type guard where if check(a) returns true, then a is not falsy.
Or you could, you know, just check the value itself:
type Falsy = null | undefined | false | 0 | ''
const a: string | Falsy = 'test'
if (a) {
// a is inferred to be just "string"
}
TL;DR
your "check" function does some clever trickery to reuse the Boolean function to do exactly what a simple if() can already do.
Just check the truthiness of a value with if(value) and move on
<edit>
I missed an important part of the question:
Could someone please explain how this function would operate on an arrays inputs please? a sort of walk through of the syntax in this exampled would be helpful. Thanks.
in that case, your check function can be used with the filter method for some nice type inference:
const arr = ['a', false, 123, 0, '', null, undefined];
arr.filter(Boolean).forEach(value => {
// value is possibly falsy even though the `filter` makes sure it isn't
});
const check = (Boolean as any) as <T>(
a: T | false | undefined | null | ''
) => a is T;
arr.filter(check).forEach(value => {
// value is string | number | true
});
This is because Typescript can't tell that the Boolean function also works just fine as a guard. So that's the one good use-case for your check function, though I'd rename it to isTruthy.
Consider following code as example:
class Something {
public getItems(): string | string[] {
return items;
}
}
Is it Possible to access to the array prototype method on calling getItems() ?
Something like this ?
let something = new Something();
const items = something.getItems();
items.filter(i => i.length > 2); // here for example
The only issue why you cant is the type getItems returns and it is string | string[]. The string part is a problem, as string has no Array prototype. In order to use the value returned by getItems we need to narrow the type by type guard.
const items = something.getItems(); // items is string | string[]
if (typeof items !== 'string') {
// now type is narrowed to string[]
items.filter(i => i.length > 2); // fully type safe
} else {
// here you have a string
}
We can also do that more consist by narrowing the structure to the array of strings also for string.
const items = something.getItems(); // items is string | string[]
// in line below I am bringing both possible values into array structure
const finalItems = typeof items === 'string' ? [items] : items;
finalItems.filter(i => i.length > 2); // fully type safe
The important in above is [items] where I put single string value into array, thanks that finalItems has only one type - string[]
concat can take arguments of either type: string or array of string. In either case, it gives the same result (an array):
let filtered = [].concat(items).filter(i => i.length > 2);
I would however advise to adjust the interface of Something so that it already takes care of this, and only returns arrays, even if only having one string.
It is possible, but only one thing that you have to consider. getItems() method returns array or string. If it returns a string, your application will throw an error that is cannot read property of undefined. To prevent this, your method should always return an array.
You can do like this,
let something = new Something();
const items: any = something.getItems();
items.filter(i => i.length > 2);
In this case, compilation error will be supressed.
But for the code to work at runtime, the returning value should be an string[].
If not TypeError: items.filter is not a function will be thrown.
I've been playing this for a while. Why is val_a changed when I run the function to sort val b? How can I get around this? When I run the function separately they work, but when I run them together something goes wrong somewhere.
var GLOBALS = {"items":[]};
var val_a;
var val_b;
GLOBALS.items=[
{"thing":"one","val_a":0.5,"val_b":0.2},
{"thing":"two","val_a":0.2,"val_b":0.3},
{"thing":"three","val_a":0.3,"val_b":0.1}];
val_a = GLOBALS.items.sort(function (a, b) {
a=parseFloat(a.val_a);
b=parseFloat(b.val_a);
if (a < b) {
return -1;
}
if (a > b) {
return 1;
}
return 0;
});
val_b = GLOBALS.items.sort(function (a, b) {
a=parseFloat(a.val_b);
b=parseFloat(b.val_b);
if (a < b) {
return -1;
}
if (a > b) {
return 1;
}
return 0;
});
console.log("val_a",val_a);
console.log("val_b",val_b);
The reason you're seeing what you're seeing is that val_a and val_b just contain references to the same array, which is also referenced from GLOBALS.items. sort changes the state of the array that all three of those variables are pointing to.
Variables contain values, not objects. When dealing with objects (including arrays), the value in the variable is a reference to the object, which actually exists elsewhere in memory. Think of that reference as a number telling the JavaScript engine where the object is elsewhere in memory. (More below.)
If you want three separate arrays (the original items, then a copy sorted by val_a, and another sorted by val_b), you want to make a shallow copy of the array before sorting it, which you can do with slice:
val_a = GLOBALS.items.slice().sort(...);
About the array being elsewhere in memory, here's a simpler example:
var a = [42]; // A variable with a reference to an array, which is
// elsewhere in memory
That gives us this in memory:
+---------+
a<REF5512>--------->| (array) |
+---------+
| 0: 42 |
+---------+
(That REF5512 is just completely made up to make the point that a reference is a value. We never see the raw reference values.)
Then if we do:
var b = a; // A copy of that same reference
That gives us this in memory:
a<REF5512>---+
| +---------+
+----->| (array) |
| +---------+
b<REF5512>---+ | 0: 42 |
+---------+
If we change the state of the array (for instance, by sorting it), naturally that change is visible from either variable, as they're both referring to the same array:
b[0] = 67;
gives us
a<REF5512>---+
| +---------+
+----->| (array) |
| +---------+
b<REF5512>---+ | 0: 67 | State of the array changed
+---------+
slice creates a new array with a copy of the values in the old array (just the values; the objects in your array aren't copied). So for instance:
b = a.slice();
Gives us:
+---------+
a<REF5512>--------->| (array) |
+---------+
| 0: 67 |
+---------+
+---------+
b<REF7341>--------->| (array) |
+---------+
| 0: 67 |
+---------+
Note how the reference in b is no longer the same as in a.
Side note: Your sort callbacks can be much simpler:
val_a = GLOBALS.items.sort(function (a, b) {
return parseFloat(a.val_a) - parseFloat(b.val_a);
});
The sort callback just has to return a value less than zero (it doesn't have to be specifically -1), greater than zero (it doesn't have to be specifically 1), or equal to zero.
I have a 2d array in the format
emi_309 | present | weak | 6
emi_310 | present | strong | 9
emi_319 | present | medium | 8
emi_315 | present | weak | 5
I want to check if a value exists in the first column using a simple function
E.g, check if emi_77 exists in the first column
I came across $.inArray(value, array) but this function is for a 1d array only.
Is there something similar for 2d array
Yes, if you do a combination of $.inArray and $.map:
if ($.inArray(value, $.map(arr, function(v) { return v[0]; })) > -1) {
// ...
}
Can use $.grep to create new array and check it's length
var val='emi_77';
if( $.grep( twoDarray, function(item){ return item[0]===val; }).length){
/* item exists*/
}
$.grep will not affect original array
API Reference: http://api.jquery.com/jQuery.grep/
Here is how I do it simply, without Jquery.
for (var i = 0; i <array.length; i++) {
if(array[i][0] == 'emi_77'){
alert("exists");
}
}
If you wanna take this further, you can use the function below;
function IsIn2D(str,order,array){
for (var i = 0; i <array.length; i++) {
return array[i][order] == str;
}
}
where;
array is the array you want to search
str is the string you want to check if it exists
order is the order of the string in the internal arrays
for example, by appling this to the question's sample 2D array:
emi_309 | present | weak | 6
emi_310 | present | strong | 9
emi_319 | present | medium | 8
emi_315 | present | weak | 5
IsIn2D('emi_77',0,array); /*returns false*/
IsIn2D('emi_315',0,array); /*returns true*/
Check this methods to search from 2D array.
Just check with converting to string and compare.
function isArrayItemExists(array , item) {
for ( var i = 0; i < array.length; i++ ) {
if(JSON.stringify(array[i]) == JSON.stringify(item)){
return true;
}
}
return false;
}