EDIT - 2 elegant solutions
Two elegant solutions, from Pranav and Bekim. Thanks, both tested and worked perfectly.
One
for(var x in data)data[x].name == "other" ? data.push( data.splice(x,1)[0] ) : 0;
Two
var res = data.slice(),
len = res.length;
for (var i = 0; i < len; i++) {
if (res[i].name == 'other') {
res.push(res.splice(i, 1)[0]);
i--;
len--;
}
}
JS TOOLS IM USING
Angular 1.5.6, lodash 4.1x
Here is the scenario I have an array of objects sorted alphabetically e.g. sortedData below etc.. However, within that array is also the catch all Other which is obviously sorted alphabetically as well. I want to remove other from the array and then move to the end of array without messing with the current sorted array.
NOTE
My current approach below works but is ugly. Does JS, angular or lodash have something more elegant?
var data = [
{id:1,name:'apple'},
{id:2,name:'banana'},
{id:3,name:'other'},
{id:4,name:'tomato'},
{id:5,name:'strawberry'}
];
function move(array, fromIndex, toIndex) {
array.splice(toIndex, 1, array.splice(fromIndex, 1)[0]);
return array;
}
var moved = move(
data,
_.findIndex(data, ['name', 'other']),
Object.keys(data).length
);
Ideal Outcome
var data = [
{id:1,name:'one'},
{id:2,name:'two'},
{id:4,name:'four'},
{id:5,name:'five'}
{id:3,name:'other'},
]
You do Array.findIndex in pure Javascript (ES6), without using any libs:
data.push(data.splice(data.findIndex(v => v.name == 'other'), 1)[0])
Array.findIndex is new, but most browsers people actually use these days have it. So you can probably use it. (Edge supports it, but not IE).
If you find the [0] ugly, you can use spread to unpack the array (that way it'll also work for more than one item with some changes):
data.push(...data.splice(data.findIndex(v => v.name == 'other'), 1))
A plain (Vanilla) JavaScript will do just fine:
for(var x in data)data[x].name == "other" ? data.push( data.splice(x,1)[0] ) : 0;
var data = [
{id:1,name:'apple'},
{id:2,name:'banana'},
{id:3,name:'other'},
{id:4,name:'tomato'},
{id:5,name:'strawberry'}
];
for(var x in data)data[x].name == "other" ? data.push( data.splice(x,1)[0] ) : 0;
console.log( data );
Use simple for loop
var data = [{
id: 1,
name: 'apple'
}, {
id: 2,
name: 'banana'
}, {
id: 3,
name: 'other'
}, {
id: 4,
name: 'tomato'
}, {
id: 5,
name: 'strawberry'
}];
// store the length of array
var len = data.length;
// iterate over all array elements
for (var i = 0; i < len; i++) {
// check value is `other`
if (data[i].name == 'other') {
// if value is other then move the object
data.push(data.splice(i, 1)[0]);
// decrement i since the element removed
i--;
// decrement len to avoid moved element
len--;
}
}
console.log(data);
Related
I'm looking for a way to find the last index of an object in Javascript from a point in an array. For example:
array.lastIndexOf(object.key, start);
So far, I haven't found a good solution for this problem. I could splice the array from the 'start' point, reverse it, and then search the sliced array for the key value, but this seems like an inefficient solution to me.
EDIT:
To illustrate the problem a little more, I'm posting the code that I used in the end to solve the problem. Essentially; what I did was I used While to loop through the previous values in the array.
getLastValue = (index) => {
const arr = [
{'d':'01-02-2017','v':'123'},
{'d':'02-02-2017'},
{'d':'04-02-2017'},
{'d':'05-02-2017','v':'456'},
...
];
let lastValue;
while (arr[index] && !arr[index].v) {
index--;
}
lastValue = arr[index];
return lastValue;
}
Personally, I wouldn't choose either solution. Here is why:
LastIndexOf:
The problem lies in the comparing of elements while searching through the array. It does compare the elements using strict equality. Therefore comparing objects will always fail, except they are the same. In OP case they are different.
Slice & reverse one-liner #adeneo
Given an array of three elements [{key: A},{key: B},{key: C}] and the lookup for the last index of key = D will give you an index of 3. This is wrong as the last index should be -1 (Not found)
Looping through the array
While this is not necessarily wrong, looping through the whole array to find the element isn't the most concise way to do it. It's efficient yes, but readability can suffer from it. If I had to choose one, I'd probably choose this one. If readability / simplicity is your friend, then below is yet one more solution.
A simple solution
We can make lastIndexOf work, we just need to make the value comparable (strict equality conform). Or simply put: we need to map the objects to a single property that we want to find the last index of using javascript's native implementation.
const arr = [ { key: "a" }, { key: "b" }, { key: "c" }, { key: "e" }, { key: "e" }, { key: "f" } ];
arr.map(el => el.key).lastIndexOf("e"); //4
arr.map(el => el.key).lastIndexOf("d"); //-1
// Better:
const arrKeys = arr.map(el => el.key);
arrKeys.lastIndexOf("c"); //2
arrKeys.lastIndexOf("b"); //1
A fast solution
Simple backwards lookup (as concise and as fast as possible). Note the -1 return instead of null/undefined.
const arr = [ { key: "a" }, { key: "b" }, { key: "c" }, { key: "e" }, { key: "e" }, { key: "f" } ];
const lastIndexOf = (array, key) => {
for(let i = array.length - 1; i >= 0; i--){
if(array[i].key === key)
return i;
}
return -1;
};
lastIndexOf(arr, "e"); //4
lastIndexOf(arr, "x"); //-1
With ES2015 and findIndex you can pass a callback to look for an objects key.
If you make a copy of the array, and reverse it, you can find the last one by subtracting that index from the total length (and 1, as arrays are zero based)
It's not very efficient, but it's one line, and works well for normally sized arrays i.e. not a million indices
var idx = arr.length - 1 - arr.slice().reverse().findIndex( (o) => o.key == 'key' );
var arr = [{key : 'not'}, {key : 'not'}, {key : 'key'}, {key : 'not'}];
var idx = arr.length - 1 - arr.slice().reverse().findIndex( (o) => o.key == 'key' ); // 2
console.log(idx)
A more efficient approach would be to iterate backwards until you find the object you're looking for, and break the loop
var arr = [{key: 'not'}, {key: 'not'}, {key: 'key'}, {key: 'not'}];
var idx = (function(key, i) {
for (i; i--;) {
if (Object.values(arr[i]).indexOf(key) !== -1) {
return i;
break;
}
} return -1;
})('key', arr.length);
console.log(idx)
I think you want something like below:
var arr = [ { key: "a" }, { key: "b" }, { key: "c" }, { key: "e" }, { key: "e" }, { key: "f" } ];
console.log(lastIndexOf("e", 2));
function lastIndexOf(keyValue, start) {
for (var i = arr.length - 1; i >= start; i--) {
if (arr[i].key === keyValue) {
return i;
}
}
return null;
}
You can do this:
reverse your array: let newArr = arr.reverse()
use findIndex: newArr.findIndex(obj => obj.d == "your String")
i used sample code, like this:
//find last index in object array
const lastIndexOf = arr.reduce((acc,cur,idx)=>cur.key==xxx?idx:acc,-1)
//find last index of object in object array
const lastIndexOfObject = arr.reduce((acc,cur,idx)=>cur.key==xxx?cur:acc,undefined)
Just try to find last index in the whole massive and compare it to start
let ind = array.lastIndexOf(object.key);
if (ind > start) {return}
let a = [
{prop1:"abc",prop2:"incomplete"},
{prop1:"bnmb",prop2:"completed"},
{prop1:"bnmb",prop2:"prajapati"},
{prop1:"zxvz",prop2:"testAJ"},
{prop1:"last",prop2:"completed"},
{prop1:"last",prop2:"incomplete"},
{prop1:"last",prop2:"incomplete11"},
{prop1:"last",prop2:"incomplete"},
{prop1:"last",prop2:"incomplete"},
{prop1:"last",prop2:"incompleteness"},
];
let findLastduplicateObjectIndex = a.map(el => el.prop2).lastIndexOf("incomplete");
I have this code :
var items = [{ value: 1, text: "one" }, { value: 2, text: "two" }, { value: 3, text: "three" }];
console.log(items.indexOf({ value: 2, text: "two" }));
And i get this result in console :
-1
But I expect to get the object index, what should I do?
Javascript's Array.indexOf does not compare the properties of the objects. It just uses === operator. And the === operator won't consider the object you are creating in the second line equal to the one you've got in the items array simply because they are two different objects.
Maybe this will help you:
if ({"a":1} === {"a":1}) alert("equal"); else alert("not equal");
The two objects are not equal. So the only solution would be to iterate on all items of the array and compare each one's properties to your new object (provided in tymeJV's answer).
There is also a built-in findIndex method only in firefox that makes this task easier.
You have to iterate and check:
var index = -1;
for (var i = 0; i < items.length; i++) {
if (items[i].value == 2 && items[i].text == "two") {
index = i;
break;
}
}
console.log(index);
A little tricky, but it could help you: JSFiddle demo.
Array.prototype.indexOfObj = function(obj) {
for(var i = 0; i < this.length; i++) {
if(JSON.stringify(this[i]) == JSON.stringify(obj)) {
return i;
}
}
return -1;
}
Usage:
console.log(items.indexOfObj({ value: 2, text: "two" }));
I need to read all the connection times. (connectionTimes)
I need to delete the line - when it will be offline more than online:
userId: 1,
connectionTimes:
[
{onlineTime:"11:10:30", offlineTime:"11:18:12"}, //delete
{onlineTime:"11:14:14", offlineTime:"11:52:41"} //delete
]
Delete user id - When times the connection will be empty.
userId: 1, //delete userid
connectionTimes:
[
//empty connection
]
I have this data structure:
var users = [];
users[0] = {
userId: 1,
connectionTimes:
[
{onlineTime:"11:10:30", offlineTime:"11:18:12"},
{onlineTime:"11:14:14", offlineTime:"11:52:41"}
]
}
users[1] = {
userId: 2,
connectionTimes:
[
{onlineTime:"8:08:14", offlineTime:"1:15:00"}
]
}
You can delete a property from an JavaScript object with the delete operator:
var sampleObject = {
"key1": "value1",
"key2": "value"
};
delete sampleObject["key2"];
or like this:
delete sampleObject.key2
See the Mozilla Developer Network JavaScript Reference for more background on the delete operator:
https://developer.mozilla.org/en/JavaScript/Reference/Operators/delete
Your specific example would look something like this:
for(var id in users) {
var user = users[id];
if (user.connectionTimes.length === 0) {
delete users[id];
break
}
for(var i=0; i<=user.connectionTimes.length; i++) {
var connectionTime = user.connectionTimes[i];
if (connectionTime.onlineTime < connectionTime.offlineTime) {
delete users[id];
break;
}
}
}
Here is a link to jsFiddle showing the code in action:
http://jsfiddle.net/Q86Jd/
There are many ways to remove things from arrays in Javascript. The mains ones I can think of are
The delete operator. Using it sets the chosen position to undefined and is similar to just setting the element to null. (The main difference is that deleting a key will cause it to be skiped when iterating with the forEach and map array methods).
var xs = [0,1,2];
delete xs[1];
console.log(xs); // [0, undefined, 2]
The splice method can remove a chunk or the array, moving the remaining elements left to fill it in.
var xs = [0,1,2,3];
xs.splice(2, 1); //index, ammount to remove
console.log(xs); // [0,1,3]
Setting the array's length property truncates it. This can be used to manually remove elements the old fashioned way when you want more control.
var xs = [0,1,2,3];
xs.length = 2;
console.log(xs); // [0,1]
xs.length = 4;
console.log(xs); // [0,1, undefined, undefined]
So in your case we might do something like this:
function filter_in_place(array, predicate){
var j=0;
for(var i=0; i<arr.length; i++){
var x = arr[i];
if(pred(x)){
arr[j++] = x;
}
}
arr.length = j;
}
for(var i=0; i < users.length; i++){
filter_in_place( users[i].connections, function(conn){
/*return if offline larger then online*/
});
}
filter_in_place(users, function(user){ return user.connections.length > 0; });
Basically you want to use something like this:
for( var i=0; i<users.length; i++) {
for( var j=0; j<users[i].connectionTimes.length; j++) {
if( users[i].connectionTimes[j].onlineTime < users[i].connectionTimes[j].offlineTime) {
delete users[i].connectionTimes[j];
j--;
}
}
if( users[i].connectionTimes.length == 0) {
delete users[i];
i--;
}
}
This is pretty straightforward. In pseudocode:
declare user, times
for each index in users
set user = users[ index ]
set times = user.connectionTimes
if times is empty
then delete users[ index ]
else
declare onlineSecs, offlineSecs
for each index2 in times
set onlineSecs = timeInSeconds( times[ index2 ].onlineTime )
set offlineSecs = timeInSeconds( times[ index2 ].offlineTime )
if offlineSecs > onlineSecs
then delete times[ index2 ]
In JavaScript the command to delete a variable is delete and can be used on arrays the same, e.g. delete someArray[ someIdx ];. The implementation of timeInSeconds will be something like this:
function timeInSeconds( timeWithColons ) {
var hrsMinsSecs = "12:34:56".split(':');
return ( parseInt( hrsMinsSecs[0] ) * 60 +
parseInt( hrsMinsSecs[1] )
) * 60 +
parseInt( hrsMinsSecs[2] )
;
}
I have a data dictionary like this:
var data = {
'text1': 1,
'text2': 2,
'text3': 3,
...
'text20': 20
];
I need to pick a random selection of those keys and then shuffle it's values. In the example, it should write something like this:
> console.log(choose(data, 5));
[ { key: 'text15', value: 8 },
{ key: 'text6', value: 3 },
{ key: 'text3', value: 15 },
{ key: 'text19', value: 6 },
{ key: 'text8', value: 19 } ]
For now I'm extracting the keys into another array and sorting by Math.random() but I'm stuck at swaping the values because no key should have the same value it initially had.
How would you swap key/values here?
Thanks
I put together a possible solution using underscore.js to simplify traversing the object and arrays in a cross browser manner:
var data = {
text1: 1,
text2: 2,
text3: 3,
text4: 4,
text5: 5,
text6: 6,
text7: 7,
text8: 8,
text9: 9,
text10: 10
};
function choose(data, num)
{
var keys = _.sortBy(
_.keys(data),
function(k)
{
return (Math.random() * 3) - 1;
}
),
results = [],
k1, k2;
if (num > keys.length) {
throw new Error('Impossible to retrieve more values than exist');
}
while (results.length < num) {
k1 = k2 || keys.pop();
k2 = keys.pop();
results.push({key:k1, value: data[k2]});
}
return results;
}
console.log(choose(data, 5));
This isn't necessarily an optimal approach but it seems to meet your requirements. I first grab all of the keys and sort them randomly. I then loop through the random keys creating a new object with one key and the following keys value. That way you'll always end up with a different value associated with each key. If you need it to work when the value of num passed in to the function == the number of keys in the data then you'll have to add a little more code - I'll leave that as an exercise for the reader :)
You can have a play with this code on jsfiddle:
http://jsfiddle.net/zVyQW/1/
You could do this:
collect names and corresponding values in two arrays names and values
shuffle both arrays independently of each other
take the first n items of both arrays and combine them
Here’s an example implementation:
Array.prototype.shuffle = function() {
for (var i=this.length-1, j, tmp; i>0; i--) {
j = Math.round(Math.random()*i);
tmp = this[i], this[i] = this[j], this[j] = tmp;
}
return this;
};
function choose(data, number) {
var names = [], values = [], pick = [];
for (var name in data) {
if (data.hasOwnProperty(name)) {
names.push(name);
values.push(data[name]);
}
}
names = names.shuffle(), values = values.shuffle();
for (var i=Math.min(number >>> 0, names.length-1); i>=0; i--) {
pick.push({key: names[i], value: values[i]});
}
return pick;
}
Been a while since this was answered, but I was working on shuffling and found the following to be by far the fastest implementation with an evenly random distribution.
It's fast because it only makes one call to Math.random on each iteration, all the rest is done by property access. It doesn't modify the array, just reassigns values.
function shuffle(a) {
var t, j, i=a.length, rand=Math.random;
// For each element in the array, swap it with a random
// element (which might be itself)
while (i--) {
k = rand()*(i+1)|0;
t = a[k];
a[k]=a[i];
a[i]=t;
}
return a;
}
It uses a combination of three functions (including the Array shuffle prototype method).
Here is the complete code:
var obj = {
"red":"RED",
"blue":"BLUE",
"green":"GREEN",
"yellow":"YELLOW",
"purple":"PURPLE"
};
Array.prototype.shuffle = function(){
for (var i = 0; i < this.length; i++){
var a = this[i];
var b = Math.floor(Math.random() * this.length);
this[i] = this[b];
this[b] = a;
}
}
obj = shuffleProperties(obj); // run shuffle
function shuffleProperties(obj) {
var new_obj = {};
var keys = getKeys(obj);
keys.shuffle();
for (var key in keys){
if (key == "shuffle") continue; // skip our prototype method
new_obj[keys[key]] = obj[keys[key]];
}
return new_obj;
}
function getKeys(obj){
var arr = new Array();
for (var key in obj)
arr.push(key);
return arr;
}
for(key in obj){
alert(key);
}
Check all post,
Best Regards.
Use an implementation of random that randomizes a discrete set of values, such as Math.rand seen here. For each index, randomize Math.rand(index, length-1) to get a list of random indexes, the location off all indices will change.
I have an array with name "ids" and some values like ['0','567','956','0','34']. Now I need to remove "0" values from this array.
ids.remove ("0"); is not working.
Here's a function that will remove elements of an array with a particular value that won't fail when two consecutive elements have the same value:
function removeElementsWithValue(arr, val) {
var i = arr.length;
while (i--) {
if (arr[i] === val) {
arr.splice(i, 1);
}
}
return arr;
}
var a = [1, 0, 0, 1];
removeElementsWithValue(a, 0);
console.log(a); // [1, 1]
In most browsers (except IE <= 8), you can use the filter() method of Array objects, although be aware that this does return you a new array:
a = a.filter(function(val) {
return val !== 0;
});
Use splice method in javascript. Try this function:
function removeElement(arrayName,arrayElement)
{
for(var i=0; i<arrayName.length;i++ )
{
if(arrayName[i]==arrayElement)
arrayName.splice(i,1);
}
}
Parameters are:
arrayName:- Name of the array.
arrayElement:- Element you want to remove from array
Here's one way to do it:
const array = ['0', '567', '956', '0', '34'];
const filtered = array.filter(Number);
console.log(filtered);
For non-trivial size arrays, it's still vastly quicker to build a new array than splice or filter.
var new_arr = [],
tmp;
for(var i=0, l=old_arr.length; i<l; i++)
{
tmp = old_arr[i];
if( tmp !== '0' )
{
new_arr.push( tmp );
}
}
If you do splice, iterate backwards!
For ES6 best practice standards:
let a = ['0','567','956','0','34'];
a = a.filter(val => val !== "0");
(note that your "id's" are strings inside array, so to check regardless of type you should write "!=")
Below code can solve your problem
for(var i=0; i<ids.length;i++ )
{
if(ids[i]=='0')
ids.splice(i,1);
}
ids.filter(function(x) {return Number(x);});
I believe, the shortest method is
var newList = ['0', '567', '956', '0', '34'].filter(cV => cV != "0")
You could always do,
listWithZeros = ['0', '567', '956', '0', '34']
newList = listWithZeros.filter(cv => cv != "0")
The newList contains your required list.
Explanation
Array.prototype.filter()
This method returns a new array created by filtering out items after testing a conditional function
It takes in one function with possibly 3 parameters.
Syntax:
Array.prototype.filter((currentValue, index, array) => { ... })
The parameters explain themselves.
Read more here.
The easy approach is using splice!!. But there's a problem, every time you remove an element your array size will constantly reduce. So the loop will skip 1 index the array size reduces.
This program will only remove every first zero.
// Wrong approach
let num = [1, 0, 0, 2, 0, 0, 3,];
for(let i=0; i<num.length; i++){
if(num[i]==0)
num.splice(i, 1);
}
console.log(num)
the output will be
[1,0,2,0,3]
So to remove all the zeros you should increase the index if you found the non-zero number.
let i = 0;
while(i<num.length){
if(num[i]==0){
num.splice(i,1);
}
else{
i++;
}
}
But there's a better way. Since changing the size of the array only affects the right side of the array. You can just traverse in reverse and splice.
for(let i=num.length-1; i>=0; i--){
if(num[i]===0)
num.splice(i,1);
}