Better way to find if item exists in JS array - javascript

I have and array which contains more then 10 000 values and somehow lodash method includes stopped working.
The example of array:
['888888111118888',
'7771117717717771']
And my method (not working) but were working yesterday as well (with lower amount of values in array)
toFind = '7771117717717771'; // this is just for example
return _.includes(arr, toFind);
and no matter if the toFind is in array the method returns "false". Any suggestions?
EDIT
so this is what I have noticed now:
console.log(data.memberList.members[0].steamID64[7]);
toFind = data.memberList.members[0].steamID64[7];
console.log(_.findIndex(data.memberList.members[0].steamID64, toFind));
and it responds with:
999999999999
-1
so how is this even possible?

Example of Array.prototype.findIndex:
let arr = [ "123", "456" ];
let index = arr.findIndex ( x => { return x === "123" } );
This will return 0 (the first index of "123"). If you have objects:
let arr = [ { val : "123" }, { val : "456" } ];
let index = arr.findIndex ( x => { return x.val === "123" } );
And so on. If you get -1, either your matcher is wrong, or it's not in the array.
I have tremendous respect for lodash, but as we get increasing native capability such as maps, sets, along with other functional higher order capabilities (filter, map, reduce, findIndex, etc), I've found my use of it diminishing.

Array.prototype.find
let result = arr.find(r => r ==='7771117717717771');
If result is defined then it exists in the array. If you are looking for an operation that runs faster than O(n) time, you should look into alternative data structures such as maps or trees

Related

Why do i get just a number from console.log(Array.push()); in Javascript? [duplicate]

Are there any substantial reasons why modifying Array.push() to return the object pushed rather than the length of the new array might be a bad idea?
I don't know if this has already been proposed or asked before; Google searches returned only a myriad number of questions related to the current functionality of Array.push().
Here's an example implementation of this functionality, feel free to correct it:
;(function() {
var _push = Array.prototype.push;
Array.prototype.push = function() {
return this[_push.apply(this, arguments) - 1];
}
}());
You would then be able to do something like this:
var someArray = [],
value = "hello world";
function someFunction(value, obj) {
obj["someKey"] = value;
}
someFunction(value, someArray.push({}));
Where someFunction modifies the object passed in as the second parameter, for example. Now the contents of someArray are [{"someKey": "hello world"}].
Are there any drawbacks to this approach?
See my detailed answer here
TLDR;
You can get the return value of the mutated array, when you instead add an element using array.concat[].
concat is a way of "adding" or "joining" two arrays together. The awesome thing about this method, is that it has a return value of the resultant array, so it can be chained.
newArray = oldArray.concat[newItem];
This also allows you to chain functions together
updatedArray = oldArray.filter((item) => {
item.id !== updatedItem.id).concat[updatedItem]};
Where item = {id: someID, value: someUpdatedValue}
The main thing to notice is, that you need to pass an array to concat.
So make sure that you put your value to be "pushed" inside a couple of square brackets, and you're good to go.
This will give you the functionality you expected from push()
You can use the + operator to "add" two arrays together, or by passing the arrays to join as parameters to concat().
let arrayAB = arrayA + arrayB;
let arrayCD = concat(arrayC, arrayD);
Note that by using the concat method, you can take advantage of "chaining" commands before and after concat.
Are there any substantial reasons why modifying Array.push() to return the object pushed rather than the length of the new array might be a bad idea?
Of course there is one: Other code will expect Array::push to behave as defined in the specification, i.e. to return the new length. And other developers will find your code incomprehensible if you did redefine builtin functions to behave unexpectedly.
At least choose a different name for the method.
You would then be able to do something like this: someFunction(value, someArray.push({}));
Uh, what? Yeah, my second point already strikes :-)
However, even if you didn't use push this does not get across what you want to do. The composition that you should express is "add an object which consist of a key and a value to an array". With a more functional style, let someFunction return this object, and you can write
var someArray = [],
value = "hello world";
function someFunction(value, obj) {
obj["someKey"] = value;
return obj;
}
someArray.push(someFunction(value, {}));
Just as a historical note -- There was an older version of JavaScript -- JavaScript version 1.2 -- that handled a number of array functions quite differently.
In particular to this question, Array.push did return the item, not the length of the array.
That said, 1.2 has been not been used for decades now -- but some very old references might still refer to this behavior.
http://web.archive.org/web/20010408055419/developer.netscape.com/docs/manuals/communicator/jsguide/js1_2.htm
By the coming of ES6, it is recommended to extend array class in the proper way , then , override push method :
class XArray extends Array {
push() {
super.push(...arguments);
return (arguments.length === 1) ? arguments[0] : arguments;
}
}
//---- Application
let list = [1, 3, 7,5];
list = new XArray(...list);
console.log(
'Push one item : ',list.push(4)
);
console.log(
'Push multi-items :', list.push(-9, 2)
);
console.log(
'Check length :' , list.length
)
Method push() returns the last element added, which makes it very inconvenient when creating short functions/reducers. Also, push() - is a rather archaic stuff in JS. On ahother hand we have spread operator [...] which is faster and does what you needs: it exactly returns an array.
// to concat arrays
const a = [1,2,3];
const b = [...a, 4, 5];
console.log(b) // [1, 2, 3, 4, 5];
// to concat and get a length
const arrA = [1,2,3,4,5];
const arrB = [6,7,8];
console.log([0, ...arrA, ...arrB, 9].length); // 10
// to reduce
const arr = ["red", "green", "blue"];
const liArr = arr.reduce( (acc,cur) => [...acc, `<li style='color:${cur}'>${cur}</li>`],[]);
console.log(liArr);
//[ "<li style='color:red'>red</li>",
//"<li style='color:green'>green</li>",
//"<li style='color:blue'>blue</li>" ]
var arr = [];
var element = Math.random();
assert(element === arr[arr.push(element)-1]);
How about doing someArray[someArray.length]={} instead of someArray.push({})? The value of an assignment is the value being assigned.
var someArray = [],
value = "hello world";
function someFunction(value, obj) {
obj["someKey"] = value;
}
someFunction(value, someArray[someArray.length]={});
console.log(someArray)

Array of string to array of array in Javascript

I'm wondering how to convert an array of strings
lines = ['101','010','110'];
to an array of arrays like this:
x = [
[1,0,1],
[0,1,0],
[1,1,0]'
]
I already tried
x = (lines.forEach(e => (e.split(''))))
and realized String.split doesnt mutate the current string. So my next step was to create a new array with these values.
x = new Array(lines.forEach(e => (e.split(''))))
My thoughts behind this line:
The code should take an element (e) of the lines array and apply the split funtion to it. (which is does when i console.log() it.) BUT it doesnt apply it to the new array.
Maybe the problem is, that it doesn't loop through x but maybe i overlook another fact.
You can use .map(Number) on the split() result to convert them to a Number as expected
const lines = ['101','010','110'];
const res = lines.map(l => l.split('').map(Number));
console.log(res);
[
[
1,
0,
1
],
[
0,
1,
0
],
[
1,
1,
0
]
]
Regarding your forEach solution, since forEach does not return anything, x stays undefined
You could define an empty array, and push your split() into that empty array, but using map() is a more readable/clean solution.
For more information about map() vs forEach(), please take a look at this stackoverflow answer.
As per #OstoneO's answer, Array#map() is the most appropriate method to use, hence it would be my first choice.
Array#forEach()'s return value is undefined because it's not designed to return a value; it is a loop and should be used as such:
const lines = ['101','010','110'];
const x = [];
lines.forEach( line => x.push( line.split('').map(n => +n) ) );
console.log( x );
Using Array.prototype.reduce method,
['101','010','110'].reduce((acc,val,index)=>{
acc[index] = [...val.split("").map((item)=>parseInt(item))];
return acc;
}
,[]);

Javascript array filtering and mapping

I keep asking the same question in terms of react and not getting a clear answer, so ive decided to attempt to extrapolate conceptually what i want to do and see if i cant get some answers on how i can proceed in react.
filters= ["Bill Johnson", "hasStartDocs"]
mappedArray =
[
{
name:'Larry',
docs:{
startDocs:[{...}]
},
workers:{
sales:["Bill Johnson"]
}
]
So if filter[i] has a space{' '} check all arrays under workers for strings like filter[i] and then map
filteredMappedArray based on that
if filter[i] does not have a space create a new string slicing the first 3 chars of the string making the first letter of that new string lower case (thatString = "startDocs") eval(resoStatus.${thatString}.length > 0) then map filteredMappedArray like that.
so after that for every instance of [i] you would have a unique map. so if someone clicked 5 filters there would be a filteredMappedArray for each which i guess you would .concat() and .reduce() if they have the same _id.
I dont need someone to help with the string manipulation. I need a nudge in the right direction on how to utilize both filters [] and mappedArray [] to create 1 filteredMappedArray. please and thank you.
for(i=0,i<filters.length,i++){
filters.map(filter => filter.includes(' ') //map mappedArray based on rules above)
filters.map(filter => filter.includes(/^has/ //map mappedArray based on rules above)
}
this gives you
[filteredMappedArray1]
[filteredMappedArray2]
bigArray = filteredMappedArray1.concat(filteredMappedArray2)
smallArray = bigArray.forEach(map //if the map is unique delete it if the map isnt unique keep it but remove all the duplicates)
As best as I can make out, you aren't doing any mapping, just filtering the mappedArray to limit it to entries that match at least one filter in filters.
If so, here's how to do that (see comments) provided you don't have lots more filters than you have entries in mappedArray (if you did, you'd structure it differently, but that seems unlikely to me):
// The `length` check prevents filtering if there are no filters; that's usually how people
// implement filters, but remove it if that's not right in your case
const filteredArray = filters.length === 0 ? mappedArray : mappedArray.filter(entry => {
// `some` returns `true` if the callback returns a truthy value (and
// stops loopihng); it returns `false` if it reaches the end of the
// array without the callback rturning a truthy value. So basically
// this is saying "return true if any filter matches."
return filters.some(filter => {
if (filter.includes(" ")) {
// Check `workers` arrays
for (const array of Object.values(entry.workers)) {
if (array.includes(filter)) {
return true;
}
}
} else {
// Check `docs` for at least one entry for the given type
const key = /*...strip leading three, change case of fourth...*/;
const array = entry.docs.key];
if (array && array.length > 0) { // There's at least one entry
return true;
}
}
return false;
});
});

Javascript - Workaround for flatmap

So I was looking for some workaround for flat map as it doesn't work on IE and I find this one:
But I don't really understand why does it work
var gadjets = [
{computers:['asus', 'hp'],
sellphones:['Galaxy', 'Nokia']
},
{computers:['dell', 'insys'],
sellphones:['iphone', 'samsung']
}
];
const getValues = gadjets.reduce((acc, gadjet) => acc.concat(gadjet[computers]), []) // instead of gadjets.flatMap(gadjet=> gadjet[computers])
This code returns:
['asus','hp','dell','insys']
But shouldn't it return:
['asus','hp'],['dell', 'insys']
This is because reduce adds up the elements you give it. For example, take the following code:
let arr = [1,2,3,4,5];
console.log(arr.reduce((before, value)=>before+value));
This code takes each value and adds it to before. It then passes that added value into the next iteration of reduce, in the before variable.
In your code, you were passing an array into before, or in your case acc, and concatenates (merge) a new array from gadgets['computers'] and returns that array. This creates a list of the computers from the array of objects.
More info on reduce here.
But shouldn't it return
I'm not sure what you're trying to show us there, but if you mean
[['asus','hp'],['dell', 'insys']]
then no, it shouldn't. concat flattens arrays you pass it (to a single level):
const a = [].concat(['asus','hp'], ['dell', 'insys']);
console.log(a); // ["asus", "hp", "dell", "insys"]
So acc.concat(gadjet[computers]) flattens out each of those computers arrays into a new array, which is the accumulation result of the reduce.
In case you want the output to be array of arrays. Try this:
var gadjets = [
{ computers: ["asus", "hp"], sellphones: ["Galaxy", "Nokia"] },
{ computers: ["dell", "insys"], sellphones: ["iphone", "samsung"] }
];
const groupBy = key => {
let res = gadjets.reduce((objectsByKeyValue, obj) => {
let arr = [];
arr.push(obj[key]);
return objectsByKeyValue.concat(arr);
}, []);
return res;
};
console.log(groupBy("computers"));

How to create a list of unique items in JavaScript? [duplicate]

This question already has answers here:
Get all unique values in a JavaScript array (remove duplicates)
(91 answers)
Closed 1 year ago.
In my CouchDB reduce function I need to reduce a list of items to the unique ones.
Note: In that case it's ok to have a list, it will be a small number of items of string type.
My current way is to set keys of a object, then return the keys of that object
since the place the code can't use things like _.uniq for example.
I'd like to find a more elegant way to spell it than this.
function(keys, values, rereduce) {
// values is a Array of Arrays
values = Array.concat.apply(null, values);
var uniq = {};
values.forEach(function(item) { uniq[item] = true; });
return Object.keys(uniq);
}
The best method seem to be using ES6 and Set. Single line and faster* than above according to fiddle
const myList = [1,4,5,1,2,4,5,6,7];
const unique = [...new Set(myList)];
console.log(unique);
*tested in safari
2021 answer:
const unique = (arr) => [...new Set(arr)];
unique([1, 2, 2, 3, 4, 4, 5, 1]); // [1, 2, 3, 4, 5]
Here you just create a set from the given array and then convert it back to the array.
I measured performance and it's almost twice faster now than the approach proposed in the old answer I posted before. Also, it's just a one-liner.
Updated fiddle
Old answer just for the record:
Commonly, the approach you used is a good idea.
But I could propose a solution that will make the algorithm a lot faster.
function unique(arr) {
var u = {}, a = [];
for(var i = 0, l = arr.length; i < l; ++i){
if(!u.hasOwnProperty(arr[i])) {
a.push(arr[i]);
u[arr[i]] = 1;
}
}
return a;
}
As you can see we have only one loop here.
I've made an example that is testing both your and my solutions. Try to play with it.
An alternative that's suitable for small lists would be to ape the Unix command line approach of sort | uniq:
function unique(a) {
return a.sort().filter(function(value, index, array) {
return (index === 0) || (value !== array[index-1]);
});
}
This function sorts the argument, and then filters the result to omit any items that are equal to their predecessor.
The keys-based approach is fine, and will have better performance characteristics for large numbers of items (O(n) for inserting n items into a hashtable, compared to O(n log n) for sorting the array). However, this is unlikely to be noticeable on small lists. Moreover, with this version you could modify it to use a different sorting or equality function if necessary; with hash keys you're stuck with JavaScripts notion of key equality.
This should work with anything, not just strings:
export const getUniqueList = (a: Array<any>) : Array<any> => {
const set = new Set<any>();
for(let v of a){
set.add(v);
}
return Array.from(set);
};
the above can just be reduced to:
export const getUniqueValues = (a: Array<any>) => {
return Array.from(new Set(a));
};
:)
To get unique objects, you can use JSON.stringify and JSON.parse:
const arr = [{test: "a"}, {test: "a"}];
const unique = Array.from(new Set(arr.map(JSON.stringify))).map(JSON.parse);
console.log(unique);
Using Object.keys will give you strings if you put in integer arguments (uniq([1,2,3]) => ['1','2','3']. Here's one with Array.reduce:
function uniq(list) {
return list.reduce((acc, d) => acc.includes(d) ? acc : acc.concat(d), []);
}
This is an old question, I know. However, it is at the top of some google searches, so I wanted to add that you can combine the answers from #RobHague and #EugeneNaydenov using the following:
function unique(arr) {
const u = {};
return arr.filter((v) => {
return u[v] = !u.hasOwnProperty(v);
});
};
You can also ignore undefined values (often handy) by adding:
function unique(arr) {
const u = {};
return arr.filter((v) => {
return u[v] = (v !== undefined && !u.hasOwnProperty(v));
});
};
You can play with this solution here: https://jsfiddle.net/s8d14v5n/
I find the other answers to be rather complicated for no gain that I can see.
We can use the indexOf method of the Array to verify if an item exists in it before pushing:
const duplicated_values = ['one', 'one', 'one', 'one', 'two', 'three', 'three', 'four'];
const unique_list = [];
duplicated_values.forEach(value => {
if (unique_list.indexOf(value) === -1) {
unique_list.push(value);
}
});
console.log(unique_list);
That will work with any type of variable as well, even objects (given the identifier actually reference the same entity, merely equivalent objects are not seen as the same).
what about
function unique(list) {
for (i = 0; i<list.length; i++) {
for (j=i+1; j<list.length; j++) {
if (list[i] == list[j]) {
list.splice(j, 1);
}
}
}
}

Categories