Im wondering whats the time complexity of turning iterables for example MapIterator into an array.
lets say I have this code:
const Component = ({ map }) => {
return (
<Fragment>
{Array.from(map.values()).map(item => <div key={item.key}>{item.name}</div>)}
</Fragment>
)
}
what is the time complexity for each Array.from(), is it what Im thinking O(n) or is it because its an MapIterator somehow converted from arraylike to array quicker.
My use case is that I want to save items (which need to be accessed) as a map for performance issues, but I have to run through them as an array.
for this questions purpose I can save in state or use selectors or stuff like that
what do you guys think?
You are correct that Array.from() is O(n). If you're concerned about performance, the easiest thing you can do to improve is to not iterate the values twice. Array.from() already accepts a map function as a second optional argument:
Array.from(map.values(), ({ key, name }) => <div key={key}>{name}</div>)
Related
I am trying to use data I fetch from an Api in a react project. The api returns an array with only one object, but that object has many properties I need to use in the jsx.
My question is more about efficiency/best practice. Should I map the array or just call index 0.
This is within the return of the component. Just to reiterate the array only has the ONE object which has a bunch of properties.
array.map((obj) => (
<p>{obj.id}</p>
<p>{obj.name}</p>
<p>{obj.other}</p>
<p>{obj.title}</p>
))
or
<p>{array[0].id}</p>
<p>{array[0].name}</p>
<p>{array[0].other}</p>
<p>{array[0].title}</p>
If the API is not going to change, and always return an array with an entry, just set your state accordingly:
fetch('/api')
.then(response => response.json())
.then(data => setSomething(data[0]))
If the API can return multiple entries, you will need .map() for semantics.
Calling index 0 would be slightly more performant than starting an iteration, getting the one object, then stopping the iteration. However if you will ever expect there to be more than one object then you will need map.
Is there a specific reason why is better to use .map than for loops in React?
I'm working on a project where all arrays are being iterated with for loops but I'm convinced that is better and good practice to use .map because it creates a copy of the array, that to my understanding is better practice but I can't find a specific reason.
Is there a specific reason why is better to use .map than for loops in React?
If you're just iterating, map is the wrong tool. map is for mapping arrays: producing a new array based on the values from the previous array. Someone somewhere is teaching map as an iteration tool, unfortunately, doing their students a disservice. (I wish I knew who it was so I could have a word.) Never do this:
// Don't do this
myArray.map(entry => {
// ...do something with `entry`...
});
For iteration, it's a choice between a for loop, a for-of loop, and the forEach method. (Well, and a few other things; see my answer here for a thorough rundown.)
For instance, using forEach
myArray.forEach(entry => {
// ...do something with `entry`...
});
vs. using for-of:
for (const entry of myArray) {
// ...do something with `entry`...
}
(Those aren't quite equivalent. The former has to be an array. The latter can be any iterable object.)
The reason you may see map a lot in React is that you're frequently mapping things in React, in at least two ways:
Mapping from raw data to JSX elements, like this:
return (
<div>
{myData.map(({id, name}) => <div key={id}>{name}</div>)}
</div>
);
Since that's a mapping operation, with the array created by map being used to provide the contents of the outer div, map is the right choice there.
Mapping from old state to new state, like this:
const [myArray, setMyArray] = useState([]);
// ...
setMyArray(myArray.map(obj => {...obj, update: "updated value"}));
Since that again is a mapping operation, creating a new array to set as the myArray state member, map is the right choice.
...but I'm convinced that is better and good practice to use .map because it creates a copy of the array...
If you want a copy/updated version of the array, yes, map is a good choice. It's more concise than the equivalent for loop (or even for-of):
const newArray = oldArray.map(entry => /*...update...*/);
vs.
// Probably not best practice unless you have conditional logic
// in the loop body that may or may not `push` (or similar)
const newArray = [];
for (const entry of oldArray) {
newArray.push(/*...update...*/);
}
.map() maps each array value to a new value, and returns a brand new array.
In React.js context, .map() can be used to map each array item to a piece of JSX fragment.
for loop also iterates over an array, just like .map(). The major difference is that you can specify custom computation with for loop. Whereas .map() is specifically designed for mapping
Recently I was doing some sort of filter to an array and I came up with two cases and I don't know which one to use.
In terms of performance, which is better to use.
arr.filter(someCondition).map(x => x.something)
Or
arr.map(x => {
if(someCondition) {
return x.something
}
})
One thing to notice is that I'm using react, so have undefined values in the returning array (don't return any thing inside .map), it's totally acceptable.
This involves a lot of questions like, how many elements you have in the array and how many you of then will pass the condition and that is what make be wonder which one is better.
So, considering n elements and cases where all elements pass the condition and also that no elements pass the condition, which one have better performance?
.filter().map() OR .map with if inside?
First: It's really unlikely to matter.
But: Only because it's okay in your case to have undefined in the result array, the second one will be faster.
Let's compare:
The first way, filter has to make a pass through the entire array, creating a new array with the accepted entries, then map has to make a pass through the new array and make a new array of the something properties.
The second way, map makes one pass through the array, creating a new array with the something properties (or undefined for the ones that would have been filtered out).
Of course, this assumes you aren't just offloading the burden elsewhere (e.g., if React has to do something to ignore those undefineds).
But again: It's unlikely to matter.
I would probably use a third option: Build the result array yourself:
const result = [];
for (const x of arr) {
if(someCondition) {
result[result.length] = x.something;
}
}
That still makes just one pass, but doesn't produce an array with undefined in it. (You can also shoehorn that into a reduce, but it doesn't buy you anything but complexity.)
(Don't worry about for-of requiring an iterator. It gets optimized away in "hot" code.)
You could use reduce function instead of map and filter and it wouldn't return you undefined like when you use map and if.
arr.reduce((acc, x) => (someCondition ? [...acc, x.something] : acc), [])
or
arr.reduce((acc, x) => {
if (someCondition) {
acc.push(x.something);
return acc;
} else return acc;
}, []);
As #briosheje said smaller array would be a plus. As it reduces number of rerendering in your React app where you use this array and it is unnecessary to pass undefined. Reduce function would be much more efficient, I would say.
If you are wondering why I have written 1st one using spread operator and 2nd one without it is because the execution time taken for 1st one is more compared to 2nd one. And that is due to spread operator as it is cloning 'acc'. So if you want lesser execution time go for 2nd one or else if you want lesser LOC go for 1st one
This question already has answers here:
In an array of objects, fastest way to find the index of an object whose attributes match a search
(19 answers)
Closed 3 years ago.
In 2019, if I am handling an array of objects, with a length north of say 15000 and I need to find an index of an object by value, which of the following methods is going to be my best option performance-wise?
Six year old 'answer': In an array of objects, fastest way to find the index of an object whose attributes match a search
findIndex
array.findIndex(object => foo === object.id);
Array.prototype.map
array.map(object => object.id).indexOf(foo);
Conceptually, these two snippets accomplish the same goal, but they go about it very different ways. To understand how the two solutions are different, let's first look at findIndex:
The findIndex method executes the callback function once for every array index 0..length-1 (inclusive) in the array until it finds one where callback returns a truthy value.
emphasis mine
In other words, it will stop as soon as it finds the item you're looking for. indexOf has a similar behavior, because it will return the index of the first item found.
On the other hand, looking at map:
map calls a provided callback function once for each element in an array, in order, and constructs a new array from the results.
emphasis mine
In other words, map doesn't care what item you're search for. Even if the item you're looking for is the first item in the array, map will still loop through 14999 other items to create a new array of id's. This means you'll end up doing quite a lot more work to achieve the same results, both in terms of temporal complexity (more time needed to loop through all those items) and spatial complexity (more memory needed to store that temporary array).
Side note: The above is not necessarily true if you use iterators / generators, which can sort of 'look ahead' in a sense to see if more work is needed. But I think this is outside the scope of this question.
However, if you're really concerned about performance, it's always a good idea to run a test for yourself. Here's a quick benchmark to demonstrate the relative performance of the two implementations. On my machine, I get findIndex: 0.023ms / map+indexOf: 0.572ms. Your mileage may vary somewhat:
var suite = new Benchmark.Suite();
const haystack = [];
let needle;
suite
.add('findIndex', () => haystack.findIndex(o => o.id === needle))
.add('map+indexOf', () => haystack.map(o => o.id).indexOf(needle))
.on('start', () => {
for (let i = 0; i < 15000; i++) {
haystack.push({
id: Math.random().toString(36).substring(7)
});
}
console.log('Starting test.');
})
.on('cycle', () => {
needle = haystack[Math.floor(Math.random() * haystack.length)].id;
})
.on('complete', () => {
console.log('Test results (lower is better):')
suite.forEach((bench) => {
console.log(` ${bench.name}: ${(bench.stats.mean * 1000).toFixed(3)}ms`);
});
})
.run({
'async': true
});
<script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/platform/1.3.5/platform.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/benchmark/2.1.4/benchmark.min.js"></script>
I've a react application, and i've found a bug where i'm filtering an array for searching the current item the user has selected and then i'm going to do stuff with it...but, didn't know that filter function return a reference to the array's item, so, every change i made to the selected item i'm doing the same to the prop array.
const pippo = maledettoArray.filter(item => item.id === idInfame)[0];
How can i filter the prop array to get the specific item without changing it?
You can use find method instead of filter which returns first match and exits the loop.
const pippo = maledettoArray.find(item => item.id === idInfame)
To create shallow copy of the object you can use Object.assign or spread syntax.
const clone = {...pipo}
If you want to create deep copy of the nested object then you could use Lodash _.cloneDeep(value) method.
First of all, I would recommend you use the find function instead of filter to avoid the empty array return and undefined at [0]
Secondly, yes, the object reference would be returned. To avoid this, you can use Object.assign({}, originalObject) or using the spread syntax {...originalObject} . A potential problem would be with nested objects which could still be a problem.
Probably this article can help you in such case https://medium.com/#Farzad_YZ/3-ways-to-clone-objects-in-javascript-f752d148054d