javascript indexOf not working with list data - javascript

I have a list of index positions in a variable called positions, I want to find the index of one of the positions like...
positions[0]
(2) [0, 0]
positions.indexOf([0,0]);
-1
var item = positions[0]
undefined
item
(2) [0, 0]0: 01: 0length: 2__proto__: Array(0)concat: ...
positions.indexOf(item);
0
What would be the way to use indexOf in this case?

The indexOf method uses strict comparison to find its match. This is the same comparison algorithm used by ===, and it compares identity of object types (like Array). So when you pass [0,0], you're passing a new object with its own identity, so no match is found.
That's why it works when you search specifically for the item at index 0. You're now finding the Array with the same identity.
To make it work the way you want, you'll need to use a custom object comparison that takes into account the members of the Array. JavaScript has nothing like this built in, but you'll find solutions if you search for "deep equality comparison".
Once you have the comparison function, you can use .findIndex() to run it on each object.
var data = [0, 0];
var result = positions.findIndex(function(item) {
// return result of comparing `data` with `item`
// This simple implementation assumes that all `item`s will be Arrays.
return data.length === item.length &&
item.every(function(n, i) { return n === data[i] });
});
Strictly speaking, if the members of positions are all Arrays with numbers as members, you could use JSON.stringify() to do the comparison. But don't trust it if the members being compared are of other object types. It may seem to work, but could fail without notice.
var data = JSON.stringify([0, 0]);
var result = positions.findIndex(function(item) {
return data === JSON.stringify(item);
});

Related

How does native sort method deal with sparse arrays

When a sparse array is sorted [5, 2, 4, , 1].sort() -> [1, 2, 4, 5, empty], the empty value is always last even with callback no matter the return statement.
I'm building my own sort method as a challenge and I solved this problem by using filter method since filter skips empty values. Then iterate over filtered array and set original array's index to filtered array's values. Then I shorten the original array's length since the remaining items will be duplicates, and I can finally feed it in my sorting algorithm. Once that's done, then I set it's length back to original which adds appropriate amount of empty items at the end. Here's a snippet of code, but here's a link of the entire code
const collectUndefined = [];
// remove empty items and collect undefined
const removeSparse = this.filter(el => {
if (el === undefined) {
collectUndefined.push(el);
}
return el !== undefined;
});
const tempLength = this.length;
// reset values but will contain duplicates at the end
for (let i = 0; i < removeSparse.length; i++) {
this[i] = removeSparse[i];
}
// shorten length which will remove extra duplicates
this.length = removeSparse.length;
// sort algorithm ...
// place undefineds back into the array at the end
this.push(...collectUndefined);
// restores original length and add empty elemnts at the end
this.length = tempLength;
return this
Is the native sort implemented in this similar fashion when dealing with sparse arrays, or no.
When it comes to implementation of Array.sort you have to also ask which engine? They are not all equal in terms of how they end up getting to the final sorted version of the array. For example V8 has a pre-processing and post-processing step before it does any sorting:
V8 has one pre-processing step before it actually sorts anything and
also one post-processing step. The basic idea is to collect all
non-undefined values into a temporary list, sort this temporary list
and then write the sorted values back into the actual array or object.
This frees V8 from caring about interacting with accessors or the
prototype chain during the sorting itself.
You can find pretty detailed explanation of the entire process V8 goes through here
The actual source code for the V8 sort (using Timsort) can be found here and is now in Torque language.
The js tests for V8 Array.sort can be seen here
Bottom line however is that nothing is actually removed from the original array since it should not be. Sort is not supposed to mutate the original array. That would be super weird if you call myArray.sort() and all of a sudden it has 5 elements less from its 8 total (for example). That is not something you would find in any Array.sort specs.
Also Array.sort pays close attention to the types it sorts and orders them specifically. Example:
let arr = [4,2,5,,,,3,false,{},undefined,null,0,function(){},[]]
console.log(arr.sort())
Notice in the output above how array is first, followed by numeric values, object literal, Boolean, function, null and then undefined / empty. So if you want to really match the spec you would have to consider how different types are also sorted.

How to get first n elements of an object using lodash?

I want to get the first n key/value pairs from an object (not an array) using lodash. I found this answer for underscore, which says to use use first (doesn't exist in lodash), or to use take (only works on arrays).
Sample node session trying to get the 'a:7' and 'b:8' pairs from an object:
> var ld=require("lodash")
undefined
> var o={a:7, b:8, c:9}
undefined
> ld.keys(o)
[ 'a', 'b', 'c' ]
> ld.take(o, 2)
[]
> ld.first(o, 2)
undefined
>
Surely, there must be some easy way to do this with lodash, but for the life of me I can't find anything. Maybe I have to resort to native js?
You cannot take the first N elements of an object without writing custom code. This is because there is no ordering of the elements in objects, so if there were a library function for it, it would never be guaranteed to give you the elements you expect. Given an object like
var obj = { b: 3, y: 2, a: 1 };
it is not clear what the "first 2" refers to - do you want a and b because that is the alphabetic order? If so are they in that order or not? If not, do you want b and y because they appear first? Perhaps you want a and y because of their values being the lowest?
There is no guarantee for what you will get aside from not getting duplicates, so all of those combinations are valid. Furthermore, you can get them in any order y and a is equally valid output You may prefer one or another but it doesn't make it correct in general.
There are ways around this and but you have to accept that you need to deal with the non-order.
Pure JavaScript solution.
function firstN(obj, n) {
return Object.keys(obj) //get the keys out
.sort() //this will ensure consistent ordering of what you will get back. If you want something in non-aphabetical order, you will need to supply a custom sorting function
.slice(0, n) //get the first N
.reduce(function(memo, current) { //generate a new object out of them
memo[current] = obj[current]
return memo;
}, {})
}
var obj = { b: 2, y: 25, a: 1 }
console.log( firstN(obj, 2) );
This is using Object.keys, Array.prototype.sort, and Array.prototype.reduce
The same can be achieved with lodash but not vastly more concise than this - it would involve calling similar functionality. It can be done like this, for example:
function firstN(obj, n) {
return _.chain(obj)
.keys()
.sort()
.take(n)
.reduce(function(memo, current) {
memo[current] = obj[current];
return memo;
}, {})
.value();
}
var obj = { b: 2, y: 25, a: 1 }
console.log( firstN(obj, 2) );
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>
As you can see, it's pretty much the same as before. There can be variations on the syntax and the exact means of how you do this task, but the major points should still be there - you need to sort for consistency and then you can get any number of items.
If you look at the loadash documentation for first. It only takes in an array as its argument, and this is probably not the API to use.
See: https://lodash.com/docs/3.10.1#first
Here is 1 method you can solve it using standard Javascript API.
The catch here is that you can use the Object.keys(...)[index] API to retrieve the element's key based on their position.
Then all you need to do is just loop n number of times and using the derived key push it into another object.
var firstN = 2;
var o={a:7, b:8, c:9};
var result = {};
for (var index=0; index < firstN; index++) {
var key = Object.keys(o)[index];
result[key] = o[key];
}
console.log(result);
There is no straight-forward solution. Inspired by previous answers and comments, and reading a couple of articles about non-guaranteed properties order, I created a solution where I sort the keys first.
Sorting the keys
Here is a usable one-line approach with sorted keys (and therefore guaranteed order).
Chaining
_.chain(object).toPairs().sortBy(0).take(2).fromPairs().value()
Without chaining
_.fromPairs(_.take(_.sortBy(_.toPairs(object), 0), 2)),
Details on sorting
The sortBy(0) sorts our collection by keys (index 0). The original object is at first converted by toPairs() to an array of pairs (each pair is an array [key, value]) and then sorted by the first values of these pairs (the key has index 0 in the pair).
Important: As mentioned in previous answers and comments, the order of properties cannot be guaranteed, even in the latest ES versions. See this updated SO answer. Therefore I am sorting the keys.

Underscore uniq not working in Parse

I've got a simple array cars of Parse models of class Cars
When I do
var uniqCars = _.uniq(cars);
it doesn't work. uniqCars is exact same as cars. The length of cars is 5 and length of uniqCars is 5 (when it should be 2).
However, when I do:
var uniqCars = _.uniq(cars,
function (c) {
return c.id;
});
It works. My question is, why doesn't it work with the former and works with the latter? Why do I have to be so verbose? Is this an issue with Parse or underscore?
why doesn't it work with the former
because, if you don't pass the comparator function, it by default uses === operator to compare the objects. Quoting the _.uniq documentation,
Produces a duplicate-free version of the array, using === to test object equality. ... If you want to compute unique items based on a transformation, pass an iteratee function
When you use === (Strict Equality operator), no two objects will be the same unless they are one and the same object or same sequence of characters forming a string. For example,
console.assert(({} === {}) === false);
var obj = {};
console.assert(obj === obj);
console.assert("ab" === "a" + "b")
console.assert("ab" === 'a' + "b")
So, it is not specific to Parse but it is the expected behaviour in JavaScript.
The comparisons are made using strict equality. Unless there are multiple references to the same object in the array, they are not going to be strictly equal.
Produces a duplicate-free version of the array, using === to test object equality. In particular only the first occurence of each value is kept. If you know in advance that the array is sorted, passing true for isSorted will run a much faster algorithm. If you want to compute unique items based on a transformation, pass an iteratee function.

Is an array in JS actually filled with empty spaces or when printed is it filled with zeros?

I have an array:
mydata =[];
i am storing values in the array by using a numeric key that can be somewhat big
mydata[13525] = 1;
However if i only store one item as the above, when i print the array in the console it prints 13524 commas before the object, nevertheless the debugger tells me it has length 13526 and there is only one element in the array.
I am confused as i understood that JS arrays do not required every position to be filled and certainly this might consume a lot of memory on bigger numbers. Can you explain please?
The .length property of an array represents the highest index plus one in the array that has been assigned a value.
When you do not assign intermediate values (and thus have a sparse array), the intervening values that you have not assigned are undefined which is essentially the "nothing here" value in Javascript. The console chooses to display arrays by trying to show you every value between 0 and the end of the array which is simply not a very good way to display a sparse array that is mostly empty. That's more an artifact of a design choice in the console than anything else. One could design a different way to display contents of an array that would handle sparse arrays differently.
Arrays are most efficient if you use consecutive indexes started from 0. That's what they are mostly designed for and what many implementations are optimized for since a runtime can do some optimized things if it knows there is a sequential set of values.
If you know you're going to mostly not be using sequences of numeric indexes starting from 0 and as such the .length property is of little use to you, then perhaps a plain object with arbitrary properties is a better choice.
var mydata = {};
mydata[13525] = 1;
console.log(mydata[13525]); // 1
console.log(mydata.length); // undefined - no .length property on an object
console.log(myData); // {1: 13525}
If you only want to print any non-null value in the array, instead of printing the whole array I'd use a for loop that only prints non-null values. It'd be something like this...
for (i = 0; i < mydata.length; i++) {
if(mydata[i]!= null){
console.log(mydata[i]);
}
}
javascript array is bit different from others,
var foo = [1, 2, 3, 4, 5, 6];
foo.length = 3;
foo; // [1, 2, 3]
foo.length = 6;
foo.push(4);
foo; // [1, 2, 3, undefined, undefined, undefined, 4]
While the getter of the length property simply returns the number of elements that are contained in the array, in setter, a smaller value truncates the array, larger value creates a sparse array. Guess what the setter mydata[13525] = 1; would do.
src: Javascript Garden
Edit:
to print/use only the values present, you can do
mydata.forEach(function(v){console.log(v);});

Javascript array length incorrect on array of objects

Could someone explain this (strange) behavior? Why is the length in the first example 3 and not 2, and most importantly, why is the length in the second example 0? As long as the keys are numerical, length works. When they are not, length is 0. How can I get the correct length from the second example? Thank you.
a = [];
a["1"] = {"string1":"string","string2":"string"};
a["2"] = {"string1":"string","string2":"string"};
alert(a.length); // returns 3
b = [];
b["key1"] = {"string1":"string","string2":"string"};
b["key2"] = {"string1":"string","string2":"string"};
alert(b.length); // returns 0
One thing to note is that there is a difference between regular arrays and associative arrays. In regular arrays (real arrays), the index has to be an integer. On the other hand, associative arrays can use strings as an index. You can think of associative arrays as a map if you like. Now, also note, true arrays always start from zero. Thus in your example, you created an array in the following manner:
a = [];
a["1"] = {"string1":"string","string2":"string"};
a["2"] = {"string1":"string","string2":"string"}
Javascript was able to convert your string indexes into numbers, hence, your code above becomes:
a = [];
a[1] = {"blah"};
a[2] = {"blah"};
But remember what i said earlier: True arrays start from zero. Therefore, the javascript interpreter automatically assigned a[0] to the undefined. Try it out in either firebug or the chrome/safari console, and you will see something like this when you try to print "a". You should get something like "[undefined, Object, Object]. Hence the size 3 not 2 as you expected.
In your second example, i am pretty sure you are trying to simulate the use of an associated array, which essentially is adding properties to an object. Remember associated arrays enable you to use strings as a key. So in other terms, you are adding a property to the object. So in your example:
b["key1"] = {"string1":"string","string2":"string"};
this really means:
b.key1 = {"string1":"string","string2":"string"};
Initializing b =[] simply creates an array, but your assignment doesn't populate the array. It simply gives "b" extra properties.
length returns 1 + the largest integer key in the object.
In a the largest key is 2 so 1+2 is 3.
In b there are no integer keys (the keys there are key1 and key2 which cannot be converted into ints) so Javascript assumes that the largest key is -1, and 1 + -1 yields 0.
This program will help you see that:
a = [];
a["1"] = {};
a["4"] = {};
alert(a.length); // Prints 5
From the ECMAScript standard, ECMA-262, 5th ed.
15.4.5.2 length
The length property of this Array object is a data property whose value is always numerically greater than the name of every deletable property whose name is an array index.
Note the length property of an array only takes into account array indices, which are integers; setting other properties doesn't affect length.
For an array, a["3"] is equivalent to a[3] (this behavior is specified by ยง 15.4.5.1); 3 is an array index rather than a property. Thus setting a["3"] affects the array's length. b["key1"] is equivalent to b.key1. Setting properties don't affect the length of a collection.

Categories