This question already has answers here:
Javascript compare 3 values
(7 answers)
Closed 4 years ago.
I need a way to compare 3 values in a short way like this:
'aaa'=='aaa'=='aaa'
false
but as you can see, it doesn't work. Why?
With 2 values it does work obviously:
'aaa'=='aaa'
true
Comparing of first two values evaluates to true and then that true is compared with "aaa" which evaluates to false.
To make it correct you can write:
const a = 'aaa';
const b = 'aaa';
const c = 'aaa';
console.log(a === b && b === c); //true
if you have those strings stored in variables you can do
let a = 'aaa', b = 'aaa', c = 'aaa'
console.log(a === b && b === c) // true
The expression 'aaa'=='aaa'=='aaa' is evaluated as ('aaa'=='aaa')=='aaa'.
The sub-expression in parentheses evaluates to true and it becomes true=='aaa' which is false because when compares two values of different types, JavaScript first converts one of them or both to a common type. It converts the boolean true to the number 1 and the string 'aaa' to the number 0 which are, obviously, not equal.
What you need is
console.log('aaa'=='aaa' && 'aaa'=='aaa')
You can put all values in an Array and then use Array.prototype.every() function to test if all satisfy the condition defined in the callback you pass to it:
let a = 'aaa', b = 'aaa', c = 'aaa'
let arr = [a, b, c]
let arr2 = [1, 2, 1]
console.log(arr.every(i => [arr[0]].includes(i)))
console.log(arr2.every(i => [arr2[0]].includes(i)))
Also, you may get the unique values from a given sequence, and if you get a single element is because all equal:
const same = xs => new Set (xs).size === 1
const x = 'aaa'
const y = 'aaa'
const z = 'aaa'
const areSame = same ([ x, y, z ])
console.log(areSame)
const x_ = 'aaa'
const y_ = 'bbb'
const z_ = 'aaa'
const areSame_ = same ([ x_, y_, z_ ])
console.log (areSame_)
With variadic arguments
const same = (...xs) => new Set (xs).size === 1
const x = 'aaa'
const y = 'aaa'
const z = 'aaa'
const areSame = same (x, y, z)
console.log(areSame)
const x_ = 'aaa'
const y_ = 'bbb'
const z_ = 'aaa'
const areSame_ = same (x_, y_, z_)
console.log (areSame_)
Related
This question already has answers here:
ECMAScript 6 arrow function that returns an object
(6 answers)
Closed 3 years ago.
I've seen this type of JavaScript code (contrived):
const a = "hello"
const b = () => ({a})
Is this just shorthand for this equivalent fat arrow function:
const b = () => { return {a} }
Or does it have some other purpose? And what do you call these?
It seems a lot to add a construct just to save one reserved word.
I think you are asking about the right side of an arrow function.
It is just more concise, with the special rule that if the right side is a single expression to be returned, it doesn't have to be { return expr }, but just expr.
const sq = (x => x * x);
const hyphenAround = (s => `-${s}-`);
[1, 3, 5].map(a => a * a)
[1, 3, 5].reduce((a, b) => a + b)
const sq = (x => x * x);
const hyphenAround = (s => `-${s}-`);
console.log(sq(3));
console.log(hyphenAround("hello"));
console.log([1, 3, 5].map(a => a * a));
console.log([1, 3, 5].reduce((a, b) => a + b));
In your example, it is
const a = "hello"
const b = () => ({a})
which is the same as
const a = "hello"
const b = () => ({a: a})
Those are called shorthand property names.
let a = "hello"
const b = () => ({
a
});
console.log(b());
a = "a long sentence";
console.log(b());
x = 123;
y = "hello"
z = [1, 3, 5];
console.log({
x,
y,
z
});
I have been looking at this code.
https://www.learnrxjs.io/recipes/alphabet-invasion-game.html
const game$ = combineLatest(keys$, letters$).pipe(
scan < [string, Letters],
State >
((state, [key, letters]) => (
letters.ltrs[letters.ltrs.length - 1] &&
letters.ltrs[letters.ltrs.length - 1].letter === key
? ((state.score = state.score + 1), letters.ltrs.pop())
: noop,
state.score > 0 && state.score % levelChangeThreshold === 0
? ((letters.ltrs = []),
(state.level = state.level + 1),
(state.score = state.score + 1),
intervalSubject.next(letters.intrvl - speedAdjust))
: noop,
{ score: state.score, letters: letters.ltrs, level: state.level }
),
{ score: 0, letters: [], level: 1 }),
takeWhile(state => state.letters.length < endThreshold)
);
this is another code I have seen that also uses so many comas
const game$ = combineLatest(state$, player$)
.pipe(
scan<[State, [number[][], Key]], [State, [number[][], Key]]>(
([state, [brick, key]]) => (
handleKeyPress(state, brick, key),
brick = rotate(state, brick, key),
brick = collide(state, brick),
score(state),
resetKey(key),
[state, [brick, key]]
)),
tap(([state, [brick, key]]) => render(state, brick)),
takeWhile(([state, [brick, key]]) => !state.game[1].some(c => c === brck)),
finalize(renderGameOver)
);
I just doesn't understand why there are so many comas inside the function body of the scan. There are two here noop,. In my other example there is a comma in every line.
Also, I don't get why would we pass [key, letters] as an array in here.
(state, [key, letters])
I have looked throught the old Scan questions that has been asked but none of them address this comma.
This is just fancy/clever/overcomplicated usage of javascript syntax/features. First, all this comas: there is a comma operator that evaluates each of its operands (from left to right) and returns the value of the last operand. (doc: coma operator).
Simple example:
c = (
a = 1, // assigns value 1 to variable a
b = 2, // assigns value 2 to variable b
console.log(a, b), // log values to console
"c"
)
will output:
1 2 // console log
"c" // value of last operand
...and c variable will have "c" value:
console.log(c) // prints "c"
The other one is another js feature - destructuring assignment
const myFn = ([a, [b, c]]) => console.log(a, b, c);
const arrayOfThatShape = [1, [2, 3]];
myFn(arrayOfThatShape) // prints 1 2 3
So basically all this is not related with RxJS at all, nor with Functional Reactive Programming. IMO such examples are overcomplicated and introduces unnecessary noise for RxJS newcomers.
This question already has answers here:
Object comparison in JavaScript [duplicate]
(10 answers)
How to determine equality for two JavaScript objects?
(82 answers)
Closed 3 years ago.
I have an array
let arr = []
I am inserting an object into it
let a = {name:"a", age: 20}
arr.push(a);
I am checking index with below with same object assigned to different variable
let b = {name:"a", age:20}
I tried
arr.indexOf(b);
I am getting -1 I expect 0.
Please guide me
JS does object comparison (and search) by reference, not by value. Hence: for foo == bar to be true, foo and bar must be the same object, not just an object with the same properties and values:
console.log({name:"a", age:20} == {name:"a", age:20});
Hence instead of indexOf, you need something like findIndex.
let arr = [];
let a = {name:"a", age: 20};
arr.push(a);
let b = {name:"a", age:20}
const index = arr.findIndex(item => item.name === b.name && item.age === b.age);
console.log(index);
This is because, objects are reference type, the variable a and b points to two different memory locations. Thus a != b
let a = {name:"a", age: 20};
let b = {name:"a", age:20}
console.log(a == b);
JavaScript can not compare objects by default. Use Array.findIndex instead:
let arr = []
let a = {name:"a", age: 20}
arr.push(a);
let b = {name:"a", age:20}
console.log(arr.findIndex(elem => elem.name == b.name && elem.age == b.age));
Yes because Js compare by using reference not by value
let arr = [];
let a = {
name: "a",
age: 20
};
arr.push(a);
let b = {
name: "a",
age: 20
};
console.log(arr.indexOf(b));
let arr = [];
let a = {
name: "a",
age: 20
};
arr.push(a);
let b = a
console.log(arr.indexOf(b));
If I have two javascript arrays.
const a = [null,null,{a:1}];
const b = [{c:3},null,{a:3,b:2}];
I want a function which can return the following result .
[{c:3},null,{a:3,b:2}]
And the above still can apply to the following.
const a = [];
const b = [null,null,{t:4}];
I want to have the following result.
[null,null,{t:4}]
Can someone help me? Thanks !
It looks like you want to merge those objects that have the same index in the input arrays:
function merge(a, b) {
const merged = [];
for (let i = 0; i < a.length || i < b.length; i++) {
merged.push(a[i] && b[i] ? {...a[i], ...b[i]} : a[i] || b[i]);
}
return merged;
}
console.log(merge([null,null,{a:1}], [{c:3},null,{a:3,b:2}]));
console.log(merge([], [null,null,{t:4}]));
You could merge the items by checking the falsy value as well.
const
merge = (...a) => a.reduce(
(r, a) => (a.forEach((o, i) => r[i] = o || r[i] || !r[i] && o), r),
[]
);
console.log(merge([null, null, { a: 1 }], [{ c: 3 }, null, { a: 3, b: 2 }]));
console.log(merge([], [null, null, { t: 4 }]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Concat or Spread Operator should do it..
const a1 = [];
const b1 = [null,null,{t:4}];
const result1 = [...a1, ...b1];
console.log(result1);
const a2 = [null,null,{a:1}];
const b2 = [{c:3},null,{a:3,b:2}];
const result2 = [...a2, ...b2];
console.log(result2);
const a = [];
const b = [null,null,{t:4}];
const c = [...a, ...b];
console.log(c);
How about using the spread operator?
You can also use concat method to merge two arrays like so
const a = [];
const b = [null,null,{t:4}];
const c = a.concat(b);
console.log(c)
I like to split the processing into two parts:
const zipWith = (fn) => (a, b) => [...Array(Math.max(a.length, b.length))]
.map((_, i) => fn(a[i], b[i]))
const combine = zipWith ((a, b) =>
a && b ? {...a, ...b} : a ? {...a} : b ? {...b} : null)
console.log(combine([null, null, {a: 1}], [{c: 3}, null, {a: 3, b: 2}]))
console.log(combine([], [null, null, {t: 4}]))
.as-console-wrapper { max-height: 100% !important; top: 0; }
"zip" is a common name for a function which combines two arrays, index-by-index. Sometimes "zipWith" is used for an extension which takes a function for deciding how to combine those two values. This version of zipWith is slightly more complicated than other variations, as it uses the longer length of its two input arrays, rather than just the length of the first one (which might be written zip = (fn) => (a1, a2) => a1.map((a, i) => fn(a, a2[i])).)
combine calls this with a function that handles the four cases : a null / b null, a null / b non-null, a non-null / b null, and a non-null / b non-null, using or combining the values supplied.
zipWith is a quite reusable function that you might find helpful elsewhere in your app. combine is more specific to this case.
This is an advanced topic of my prior question here:
How to store data of a functional chain?
The brief idea is
A simple function below:
const L = a => L;
forms
L
L(1)
L(1)(2)
...
This seems to form a list but the actual data is not stored at all, so if it's required to store the data such as [1,2], what is the smartest practice to have the task done?
One of the prominent ideas is from #user633183 which I marked as an accepted answer(see the Question link), and another version of the curried function is also provided by #Matías Fidemraizer .
So here goes:
const L = a => {
const m = list => x => !x
? list
: m([...list, x]);
return m([])(a);
};
const list1 = (L)(1)(2)(3); //lazy : no data evaluation here
const list2 = (L)(4)(5)(6);
console.log(list1()) // now evaluated by the tail ()
console.log(list2())
What I really like is it turns out lazy evaluation.
Although the given approach satisfies what I mentioned, this function has lost the outer structure or I must mentiion:
Algebraic structure
const L = a => L;
which forms list and more fundamentally gives us an algebraic structure of identity element, potentially along with Monoid or Magma.
Left an Right identity
One of the easiest examples of Monoids and identity is number and "Strings" and [Array] in JavaScript.
0 + a === a === a + 0
1 * a === a === a * 1
In Strings, the empty quoate "" is the identity element.
"" + "Hello world" === "Hello world" === "Hello world" + ""
Same goes to [Array].
Same goes to L:
(L)(a) === (a) === (a)(L)
const L = a => L;
const a = L(5); // number is wrapped or "lift" to Type:L
// Similarity of String and Array
// "5" [5]
//left identity
console.log(
(L)(a) === (a) //true
);
//right identity
console.log(
(a) === (a)(L) //true
);
and the obvious identity immutability:
const L = a => L;
console.log(
(L)(L) === (L) //true
);
console.log(
(L)(L)(L) === (L) //true
);
console.log(
(L)(L)(L)(L) === (L) //true
);
Also the below:
const L = a => L;
const a = (L)(1)(2)(3);
const b = (L)(1)(L)(2)(3)(L);
console.log(
(a) === (b) //true
);
Questions
What is the smartest or most elegant way (very functional and no mutations (no Array.push, also)) to implement L that satisfies 3 requirements:
Requirement 0 - Identity
A simple function:
const L = a => L;
already satisfies the identity law as we already have seen.
Requirement 1 - eval() method
Although L satisfies the identity law, there is no method to access to the listed/accumulated data.
(Answers provided in my previous question provide the data accumulation ability, but breaks the Identity law.)
Lazy evaluation seems the correct approach, so providing a clearer specification:
provide eval method of L
const L = a => L; // needs to enhance to satisfy the requirements
const a = (L)(1)(2)(3);
const b = (L)(1)(L)(2)(3)(L);
console.log(
(a) === (b) //true
);
console.log(
(a).eval() //[1, 2, 3]
);
console.log(
(b).eval() //[1, 2, 3]
);
Requirement 3 - Monoid Associative law
In addition to the prominent Identify structure, Monoids also satisfies Associative law
(a * b) * c === a * b * c === a * (b * c)
This simply means "flatten the list", in other words, the structure does not contain nested lists.
[a, [b, c]] is no good.
Sample:
const L = a => L; // needs to enhance to satisfy the requirements
const a = (L)(1)(2);
const b = (L)(3)(4);
const c = (L)(99);
const ab = (a)(b);
const bc = (b)(c);
const abc1 = (ab)(c);
const abc2 = (a)(bc);
console.log(
abc1 === abc2 // true for Associative
);
console.log(
(ab).eval() //[1, 2, 3, 4]
);
console.log(
(abc1).eval() //[1, 2, 3, 4, 99]
);
console.log(
(abc2).eval() //[1, 2, 3, 4, 99]
);
That is all for 3 requirements to implement L as a monoid.
This is a great challenge for functional programming to me, and actually I tried by myself for a while, but asking the previous questions, it's very good practice to share my own challenge and hear the people and read their elegant code.
Thank you.
Your data type is inconsistent!
So, you want to create a monoid. Consider the structure of a monoid:
class Monoid m where
empty :: m -- identity element
(<*>) :: m -> m -> m -- binary operation
-- It satisfies the following laws:
empty <*> x = x = x <*> empty -- identity law
(x <*> y) <*> z = x <*> (y <*> z) -- associativity law
Now, consider the structure of your data type:
(L)(a) = (a) = (a)(L) // identity law
((a)(b))(c) = (a)((b)(c)) // associativity law
Hence, according to you the identity element is L and the binary operation is function application. However:
(L)(1) // This is supposed to be a valid expression.
(L)(1) != (1) != (1)(L) // But it breaks the identity law.
// (1)(L) is not even a valid expression. It throws an error. Therefore:
((L)(1))(L) // This is supposed to be a valid expression.
((L)(1))(L) != (L)((1)(L)) // But it breaks the associativity law.
The problem is that you are conflating the binary operation with the reverse list constructor:
// First, you're using function application as a reverse cons (a.k.a. snoc):
// cons :: a -> [a] -> [a]
// snoc :: [a] -> a -> [a] -- arguments flipped
const xs = (L)(1)(2); // [1,2]
const ys = (L)(3)(4); // [3,4]
// Later, you're using function application as the binary operator (a.k.a. append):
// append :: [a] -> [a] -> [a]
const zs = (xs)(ys); // [1,2,3,4]
If you're using function application as snoc then you can't use it for append as well:
snoc :: [a] -> a -> [a]
append :: [a] -> [a] -> [a]
Notice that the types don't match, but even if they did you still don't want one operation to do two things.
What you want are difference lists.
A difference list is a function that takes a list and prepends another list to it. For example:
const concat = xs => ys => xs.concat(ys); // This creates a difference list.
const f = concat([1,2,3]); // This is a difference list.
console.log(f([])); // You can get its value by applying it to the empty array.
console.log(f([4,5,6])); // You can also apply it to any other array.
The cool thing about difference lists are that they form a monoid because they are just endofunctions:
const id = x => x; // The identity element is just the id function.
const compose = (f, g) => x => f(g(x)); // The binary operation is composition.
compose(id, f) = f = compose(f, id); // identity law
compose(compose(f, g), h) = compose(f, compose(g, h)); // associativity law
Even better, you can package them into a neat little class where function composition is the dot operator:
class DList {
constructor(f) {
this.f = f;
this.id = this;
}
cons(x) {
return new DList(ys => this.f([x].concat(ys)));
}
concat(xs) {
return new DList(ys => this.f(xs.concat(ys)));
}
apply(xs) {
return this.f(xs);
}
}
const id = new DList(x => x);
const cons = x => new DList(ys => [x].concat(ys)); // Construct DList from value.
const concat = xs => new DList(ys => xs.concat(ys)); // Construct DList from array.
id . concat([1, 2, 3]) = concat([1, 2, 3]) = concat([1, 2, 3]) . id // identity law
concat([1, 2]) . cons(3) = cons(1) . concat([2, 3]) // associativity law
You can use the apply method to retrieve the value of the DList as follows:
class DList {
constructor(f) {
this.f = f;
this.id = this;
}
cons(x) {
return new DList(ys => this.f([x].concat(ys)));
}
concat(xs) {
return new DList(ys => this.f(xs.concat(ys)));
}
apply(xs) {
return this.f(xs);
}
}
const id = new DList(x => x);
const cons = x => new DList(ys => [x].concat(ys));
const concat = xs => new DList(ys => xs.concat(ys));
const identityLeft = id . concat([1, 2, 3]);
const identityRight = concat([1, 2, 3]) . id;
const associativityLeft = concat([1, 2]) . cons(3);
const associativityRight = cons(1) . concat([2, 3]);
console.log(identityLeft.apply([])); // [1,2,3]
console.log(identityRight.apply([])); // [1,2,3]
console.log(associativityLeft.apply([])); // [1,2,3]
console.log(associativityRight.apply([])); // [1,2,3]
An advantage of using difference lists over regular lists (functional lists, not JavaScript arrays) is that concatenation is more efficient because the lists are concatenated from right to left. Hence, it doesn't copy the same values over and over again if you're concatenating multiple lists.
mirror test
To make L self-aware we have to somehow tag the values it creates. This is a generic trait and we can encode it using a pair of functions. We set an expectation of the behavior –
is (Foo, 1) // false 1 is not a Foo
is (Foo, tag (Foo, 1)) // true tag (Foo, 1) is a Foo
Below we implement is and tag. We want to design them such that we can put in any value and we can reliably determine the value's tag at a later time. We make exceptions for null and undefined.
const Tag =
Symbol ()
const tag = (t, x) =>
x == null
? x
: Object.assign (x, { [Tag]: t })
const is = (t, x) =>
x == null
? false
: x[Tag] === t
const Foo = x =>
tag (Foo, x)
console.log
( is (Foo, 1) // false
, is (Foo, []) // false
, is (Foo, {}) // false
, is (Foo, x => x) // false
, is (Foo, true) // false
, is (Foo, undefined) // false
, is (Foo, null) // false
)
console.log
( is (Foo, Foo (1)) // true we can tag primitives
, is (Foo, Foo ([])) // true we can tag arrays
, is (Foo, Foo ({})) // true we can tag objects
, is (Foo, Foo (x => x)) // true we can even tag functions
, is (Foo, Foo (true)) // true and booleans too
, is (Foo, Foo (undefined)) // false but! we cannot tag undefined
, is (Foo, Foo (null)) // false or null
)
We now have a function Foo which is capable of distinguishing values it produced. Foo becomes self-aware –
const Foo = x =>
is (Foo, x)
? x // x is already a Foo
: tag (Foo, x) // tag x as Foo
const f =
Foo (1)
Foo (f) === f // true
L of higher consciousness
Using is and tag we can make List self-aware. If given an input of a List-tagged value, List can respond per your design specification.
const None =
Symbol ()
const L = init =>
{ const loop = (acc, x = None) =>
// x is empty: return the internal array
x === None
? acc
// x is a List: concat the two internal arrays and loop
: is (L, x)
? tag (L, y => loop (acc .concat (x ()), y))
// x is a value: append and loop
: tag (L, y => loop ([ ...acc, x ], y))
return loop ([], init)
}
We try it out using your test data –
const a =
L (1) (2)
const b =
L (3) (4)
const c =
L (99)
console.log
( (a) (b) (c) () // [ 1, 2, 3, 4, 99 ]
, (a (b)) (c) () // [ 1, 2, 3, 4, 99 ]
, (a) (b (c)) () // [ 1, 2, 3, 4, 99 ]
)
It's worth comparing this implementation to the last one –
// previous implementation
const L = init =>
{ const loop = (acc, x) =>
x === undefined // don't use !x, read more below
? acc
: y => loop ([...acc, x], y)
return loop ([], init)
}
In our revision, a new branch is added for is (L, x) that defines the new monoidal behavior. And most importantly, any returned value in wrapped in tag (L, ...) so that it can later be identified as an L-tagged value. The other change is the explicit use of a None symbol; additional remarks on this have been added a the end of this post.
equality of L values
To determine equality of L(x) and L(y) we are faced with another problem. Compound data in JavaScript are represented with Objects which cannot be simply compared with the === operator
console.log
( { a: 1 } === { a: 1 } ) // false
We can write an equality function for L, perhaps called Lequal
const l1 =
L (1) (2) (3)
const l2 =
L (1) (2) (3)
const l3 =
L (0)
console.log
( Lequal (l1, l2) // true
, Lequal (l1, l3) // false
, Lequal (l2, l3) // false
)
But I won't go into how to do that in this post. If you're interested, I covered that topic in this Q&A.
// Hint:
const Lequal = (l1, l2) =>
arrayEqual // compare two arrays
( l1 () // get actual array of l1
, l2 () // get actual array of l2
)
tagging in depth
The tagging technique I used here is one I use in other answers. It is accompanied by a more extensive example here.
other remarks
Don't use !x to test for an empty value because it will return true for any "falsy" x. For example, if you wanted to make a list of L (1) (0) (3) ... It will stop after 1 because !0 is true. Falsy values include 0, "" (empty string), null, undefined, NaN, and of course false itself. It's for this reason we use an explicit None symbol to more precisely identify when the list terminates. All other inputs are appended to the internal array.
And don't rely on hacks like JSON.stringify to test for object equality. Structural traversal is absolutely required.
const x = { a: 1, b: 2 }
const y = { b: 2, a: 1 }
console.log
(JSON.stringify (x) === JSON.stringify (y)) // false
console.log
(Lequal (L (x), L (y))) // should be true!
For advice on how to solve this problem, see this Q&A