Related
I am doing a coding challenge trying to learn merge sort and I have gotten my merge sort to handle dates properly but not an integer value. Currently it seems to outputting data at random.
EDIT: There are over a dozen different posts like the sample data listed. I am unable to sort each of those objects based on their votes from high to low
My Merge Sort Function:
function sortBy(array, key, descending = false) {
const length = array.length;
if (length === 1) {
return array;
} else if (length === 2) {
const aValue = array[0][key];
const bValue = array[1][key];
if (bValue > aValue) {
return array;
}
return [
array[0],
array[1],
];
}
const mid = Math.floor(length / 2);
const firstHalf = array.slice(0, mid);
const secondHalf = array.slice(mid, length);
const arrayOne = sortBy(firstHalf, key);
const arrayTwo = sortBy(secondHalf, key);
const merged = [];
while (arrayOne.length || arrayTwo.length) {
if (!arrayOne.length) {
merged.push(arrayTwo.shift());
continue;
}
if (!arrayTwo.length) {
merged.push(arrayOne.shift());
continue;
}
const valueOne = arrayOne[0][key];
const valueTwo = arrayTwo[0][key];
if (valueOne <= valueTwo) {
merged.push(arrayOne.shift());
} else if (valueTwo < valueOne) {
merged.push(arrayTwo.shift());
}
}
return descending ? merged.reverse() : merged;
}
Sample Data
[{
created: '2016-03-07T05:24:40.340Z',
details: 'Right now we only support single backticks. Would be nice to do triple as well... Consider supporting more or all of markdown but I\'m not sure that\'s the right direction.',
title: 'Support triple backtick codeblocks',
votes: 17,
},]
2 things I see just reading through the code:
need to access different elements of the array in the last return statement
const length = array.length;
if (length === 1) {
return array;
} else if (length === 2) {
const aValue = array[0][key];
const bValue = array[1][key];
if (bValue > aValue) {
return array;
}
return [
array[0], // needs to be array[1]
array[1], // needs to be array[0]
];
}
You need to be passing the third argument down everytime you recurse the function
const arrayOne = sortBy(firstHalf, key, descending);
const arrayTwo = sortBy(secondHalf, key, descending);
I am trying to find the indexes of all the instances of an element, say, "Nano", in a JavaScript array.
var Cars = ["Nano", "Volvo", "BMW", "Nano", "VW", "Nano"];
I tried jQuery.inArray, or similarly, .indexOf(), but it only gave the index of the last instance of the element, i.e. 5 in this case.
How do I get it for all instances?
The .indexOf() method has an optional second parameter that specifies the index to start searching from, so you can call it in a loop to find all instances of a particular value:
function getAllIndexes(arr, val) {
var indexes = [], i = -1;
while ((i = arr.indexOf(val, i+1)) != -1){
indexes.push(i);
}
return indexes;
}
var indexes = getAllIndexes(Cars, "Nano");
You don't really make it clear how you want to use the indexes, so my function returns them as an array (or returns an empty array if the value isn't found), but you could do something else with the individual index values inside the loop.
UPDATE: As per VisioN's comment, a simple for loop would get the same job done more efficiently, and it is easier to understand and therefore easier to maintain:
function getAllIndexes(arr, val) {
var indexes = [], i;
for(i = 0; i < arr.length; i++)
if (arr[i] === val)
indexes.push(i);
return indexes;
}
Another alternative solution is to use Array.prototype.reduce():
["Nano","Volvo","BMW","Nano","VW","Nano"].reduce(function(a, e, i) {
if (e === 'Nano')
a.push(i);
return a;
}, []); // [0, 3, 5]
N.B.: Check the browser compatibility for reduce method and use polyfill if required.
Another approach using Array.prototype.map() and Array.prototype.filter():
var indices = array.map((e, i) => e === value ? i : '').filter(String)
More simple way with es6 style.
const indexOfAll = (arr, val) => arr.reduce((acc, el, i) => (el === val ? [...acc, i] : acc), []);
//Examples:
var cars = ["Nano", "Volvo", "BMW", "Nano", "VW", "Nano"];
indexOfAll(cars, "Nano"); //[0, 3, 5]
indexOfAll([1, 2, 3, 1, 2, 3], 1); // [0,3]
indexOfAll([1, 2, 3], 4); // []
You can write a simple readable solution to this by using both map and filter:
const nanoIndexes = Cars
.map((car, i) => car === 'Nano' ? i : -1)
.filter(index => index !== -1);
EDIT: If you don't need to support IE/Edge (or are transpiling your code), ES2019 gave us flatMap, which lets you do this in a simple one-liner:
const nanoIndexes = Cars.flatMap((car, i) => car === 'Nano' ? i : []);
I just want to update with another easy method.
You can also use forEach method.
var Cars = ["Nano", "Volvo", "BMW", "Nano", "VW", "Nano"];
var result = [];
Cars.forEach((car, index) => car === 'Nano' ? result.push(index) : null)
Note: MDN gives a method using a while loop:
var indices = [];
var array = ['a', 'b', 'a', 'c', 'a', 'd'];
var element = 'a';
var idx = array.indexOf(element);
while (idx != -1) {
indices.push(idx);
idx = array.indexOf(element, idx + 1);
}
I wouldn't say it's any better than other answers. Just interesting.
const indexes = cars
.map((car, i) => car === "Nano" ? i : null)
.filter(i => i !== null)
This worked for me:
let array1 = [5, 12, 8, 130, 44, 12, 45, 12, 56];
let numToFind = 12
let indexesOf12 = [] // the number whose occurrence in the array we want to find
array1.forEach(function(elem, index, array) {
if (elem === numToFind) {indexesOf12.push(index)}
return indexesOf12
})
console.log(indexesOf12) // outputs [1, 5, 7]
Just to share another method, you can use Function Generators to achieve the result as well:
function findAllIndexOf(target, needle) {
return [].concat(...(function*(){
for (var i = 0; i < target.length; i++) if (target[i] === needle) yield [i];
})());
}
var target = "hellooooo";
var target2 = ['w','o',1,3,'l','o'];
console.log(findAllIndexOf(target, 'o'));
console.log(findAllIndexOf(target2, 'o'));
["a", "b", "a", "b"]
.map((val, index) => ({ val, index }))
.filter(({val, index}) => val === "a")
.map(({val, index}) => index)
=> [0, 2]
You can use Polyfill
if (!Array.prototype.filterIndex)
{
Array.prototype.filterIndex = function (func, thisArg) {
'use strict';
if (!((typeof func === 'Function' || typeof func === 'function') && this))
throw new TypeError();
let len = this.length >>> 0,
res = new Array(len), // preallocate array
t = this, c = 0, i = -1;
let kValue;
if (thisArg === undefined) {
while (++i !== len) {
// checks to see if the key was set
if (i in this) {
kValue = t[i]; // in case t is changed in callback
if (func(t[i], i, t)) {
res[c++] = i;
}
}
}
}
else {
while (++i !== len) {
// checks to see if the key was set
if (i in this) {
kValue = t[i];
if (func.call(thisArg, t[i], i, t)) {
res[c++] = i;
}
}
}
}
res.length = c; // shrink down array to proper size
return res;
};
}
Use it like this:
[2,23,1,2,3,4,52,2].filterIndex(element => element === 2)
result: [0, 3, 7]
findIndex retrieves only the first index which matches callback output. You can implement your own findIndexes by extending Array , then casting your arrays to the new structure .
class EnhancedArray extends Array {
findIndexes(where) {
return this.reduce((a, e, i) => (where(e, i) ? a.concat(i) : a), []);
}
}
/*----Working with simple data structure (array of numbers) ---*/
//existing array
let myArray = [1, 3, 5, 5, 4, 5];
//cast it :
myArray = new EnhancedArray(...myArray);
//run
console.log(
myArray.findIndexes((e) => e===5)
)
/*----Working with Array of complex items structure-*/
let arr = [{name: 'Ahmed'}, {name: 'Rami'}, {name: 'Abdennour'}];
arr= new EnhancedArray(...arr);
console.log(
arr.findIndexes((o) => o.name.startsWith('A'))
)
We can use Stack and push "i" into the stack every time we encounter the condition "arr[i]==value"
Check this:
static void getindex(int arr[], int value)
{
Stack<Integer>st= new Stack<Integer>();
int n= arr.length;
for(int i=n-1; i>=0 ;i--)
{
if(arr[i]==value)
{
st.push(i);
}
}
while(!st.isEmpty())
{
System.out.println(st.peek()+" ");
st.pop();
}
}
When both parameter passed as array
function getIndexes(arr, val) {
var indexes = [], i;
for(i = 0; i < arr.length; i++){
for(j =0; j< val.length; j++) {
if (arr[i] === val[j])
indexes.push(i);
}
}
return indexes;
}
Also, findIndex() will be useful:
var cars = ['Nano', 'Volvo', 'BMW', 'Nano', 'VW', 'Nano'];
const indexes = [];
const searchedItem = 'NaNo';
cars.findIndex((value, index) => {
if (value.toLowerCase() === searchedItem.toLowerCase()) {
indexes.push(index);
}
});
console.log(indexes); //[ 0, 3, 5 ]
Bonus:
This custom solution using Object.entries() and forEach()
var cars = ['Nano', 'Volvo', 'BMW', 'Nano', 'VW', 'Nano'];
const indexes = [];
const searchableItem = 'Nano';
Object.entries(cars).forEach((item, index) => {
if (item[1].toLowerCase() === searchableItem.toLowerCase())
indexes.push(index);
});
console.log(indexes);
Note: I did not run run all tests
I have an array of dates such as :
test = [ '2018-07-18', '2018-07-19', '2018-07-21', '2018-07-23', '2018-07-24', '2018-07-26'];
And I want to return an array of sub arrays of consecutive dates like this:
result = [['2018-07-18', '2018-07-19'], ['2018-07-21'], ['2018-07-23', '2018-07-24'], ['2018-07-26']]
I'm trying to write a snippet code:
const moment = require('moment');
let visited = [];
const alpha = test.reduce((accumlator, current_date, current_index, array) => {
let start_date = current_date;
let successive_date = array[current_index + 1];
visited.push(start_date);
if(successive_date && moment(successive_date).diff(moment(start_date), 'days') === 1
&& visited.includes(successive_date) === false) {
accumlator.concat(start_date);
accumlator.concat(successive_date);
}
if(successive_date && moment(successive_date).diff(moment(start_date), 'days') !== 1
&& visited.includes(successive_date) === false) {
accumlator.concat(successive_date);
}
return accumlator;
}, []);
console.log('alpha: ', alpha);
The result when using concat was:
alpha: []
I used push() and it returns an array such test:
alpha: [ '2018-07-18','2018-07-19','2018-07-21','2018-07-23','2018-07-23','2018-07-24''2018-07-26' ]
How can I fix this in order to get the result such as mentioned above?
You can try with:
test.reduce((acc, date) => {
const group = acc[acc.length - 1];
if (moment(date).diff(moment(group[group.length - 1] || date), 'days') > 1) {
acc.push([date])
} else {
group.push(date);
}
return acc;
}, [[]])
Output:
[
[
"2018-07-18",
"2018-07-19"
],
[
"2018-07-21"
],
[
"2018-07-23",
"2018-07-24"
],
[
"2018-07-26"
]
]
Th following helps, if the order of the dates in the array is not maintained. For example, '2018-07-18', '2018-07-19', '2018-07-17' are consecutive but scattered at the start and end of the array.
var test = [ '2018-07-18', '2018-07-19', '2018-07-21', '2018-07-23', '2018-07-24', '2018-07-26', '2018-07-17'], dateformat = "YYYY-MM-DD";
var result = test.reduce(function(acc,val){
var present, date = moment(val,dateformat);
acc.forEach(function(arr,index){
if(present) return;
if(arr.indexOf(date.clone().subtract(1,'day').format(dateformat))>-1 || arr.indexOf(date.clone().add(1,'day').format(dateformat))>-1)
{
present = true;
arr.push(val);
}
});
if(!present) acc.push([val]);
return acc;
},[]);
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.min.js"></script>
You can use this function but dates should be in sorted order.
function get_relative_dates(dates){
var format = 'YYYY-MM-DD';
var newDate = [];
dates.forEach(function(date){
var lastArr, lastDate;
if(newDate.length){
lastArr = newDate[newDate.length -1];
if(!lastArr.length)
lastArr.push(date);
else{
var lastDate = lastArr[lastArr.length -1];
if(moment(lastDate, format).add(1,'d').format(format) == date)
lastArr.push(date);
else
newDate.push([date]);
}
}
else
newDate.push([date]);
});
return newDate;
}
I have wounder`d for a while about how to get mode in array. That elements that are the same in array would be put together.
For ex. [Alex, Steven, Georg, Alice, Alex, Georg];
return would be: Alex: 2, Steven: 1, Georg: 2, Alice:1;
I wrote the code but it works only for numbers from 1 to 10. And for sure there is a better way.
(I don`t think you need my code but will paste it anyway.)
var mode = function (data){
var result1 = data.filter(function (verde) {return verde === 1});
var result2 = data.filter(function (verde) {return verde === 2});
var result3 = data.filter(function (verde) {return verde === 3});
var result4 = data.filter(function (verde) {return verde === 4});
var result5 = data.filter(function (verde) {return verde === 5});
var result6 = data.filter(function (verde) {return verde === 6});
var result7 = data.filter(function (verde) {return verde === 7});
var result8 = data.filter(function (verde) {return verde === 8});
var result9 = data.filter(function (verde) {return verde === 9});
var nyadata = [result1.length, result2.length,
result3.length, result4.length,
result5.length, result6.length,
result7.length, result8.length,
result9.length];
var nyarreymax = Math.max.apply(Math, nyadata);
if (nyarreymax === result1.length){return 1;}
if (nyarreymax === result2.length){return 2;}
if (nyarreymax === result3.length){return 3;}
if (nyarreymax === result4.length){return 4;}
if (nyarreymax === result5.length){return 5;}
if (nyarreymax === result6.length){return 6;}
if (nyarreymax === result7.length){return 7;}
if (nyarreymax === result8.length){return 8;}
if (nyarreymax === result9.length){return 9;}
else { return false;}
Hope you can help me to know code that works generally for strings and all integers.
I'm a beginner at js myself and was looking for this same solution not long ago. Here's one I found that should be what you're looking for:
function findMode(arr) {
var map = {};
for (var i = 0; i < arr.length; i++) {
if (map[arr[i]] === undefined) {
map[arr[i]] = 0;
}
map[arr[i]] += 1;
}
var greatestFreq = 0;
var mode;
for (var prop in map) {
if (map[prop] > greatestFreq) {
greatestFreq = map[prop];
mode = prop;
}
}
return mode;
}
You can try this using reduce() , see your console that shows value with counts.
Demo http://jsfiddle.net/ak69f/
var array_elements = ['Alex', 'Steven', 'Georg', 'Alice', 'Alex', 'Georg'];
var result = array_elements.reduce(function(p, c){
if (c in p) {
p[c]++;
} else {
p[c]=1;
}
return p;
}, []);
console.log(result);
Here's a simple recursive solution, which seems to be the fastest of the four answers as you can see here: http://jsperf.com/array-mode.
var a = ["Alex", "Steven", "Georg", "Alice", "Alex", "Georg"];
function getMode(a, result) {
result = result || {};
if (a.length === 0){
return result;
}
var head = a.shift();
if (result[head]){
result[head]++;
}
else{
result[head] = 1;
}
return getMode(a, result);
}
console.log(getMode(a));
First, define a new array that will hold your results.
Iterate through your names array. Inside of each loop, iterate through the results array. If the current name in your names array exists within the results array, change the value.
For example, if your names array is on the second "Alex", and you iterate through the results array and find that "Alex:1" already exists, change the value to "Alex:2" (you will have to do a little bit of string parsing for that).
If the name does not exist already, add it to the end as ":1"
Then if you want to return the mode, you will have to write another loop that finds the maximum occurrence. Have a variable that keeps track of the array position of the name with the highest number (let's say it's called maxIndex). For each item in the array, compare it to the value of the array at maxIndex. If it's higher, reset maxIndex to the current index. If it's equal to or less than, move onto the next item of the array.
I know that was very wordy, so let me know if you have any questions.
An alternative approach to this is to create a function that takes in your array, assigns each unique array value to an object property and if it already exists, increase the object properties value by one, like so;
function countArray(array){
var results = {};
for(var x = 0; x < array.length; x++){
if(results[array[x]] == undefined){
results[array[x]] = 1;
}else{
results[array[x]] += 1;
}
}
return results;
}
var checkArray = countArray(['alex', 'george', 'steve', 'alex']);
console.log(checkArray);
// outputs "Object {alex: 2, george: 1, steve: 1}"
Then you could access the results as needed by calling
console.log(checkArray.alex); // outputs 2
var numbers = [1,2,2,3,4,5];
var counts = numbers.reduce((counts, e) => { counts[e] = counts[e] ? counts[e] + 1 : 1; return counts; }, {});
var mode = Object.keys(counts).reduce((a, b) => (counts[a] > counts[b] ? a : b ));
console.log(mode);
Reduce function can help a lot in aggregations.
The way I have found is very similar to the accepted answer however I thought I would add that if no items repeat, or no single item repeats the most, there is no mode, so my function checks that and returns null if that's the case.
function calcMode(data) {
let counts = {};
data.forEach((d) => {
if (counts[d] === undefined) {
counts[d] = 0;
}
counts[d] += 1;
});
let mode,
max = 0,
repeats = 0;
Object.keys(counts).forEach((k) => {
if (counts[k] > max) {
max = counts[k];
mode = k;
repeats = 0;
} else if (counts[k] == max) repeats += 1;
});
if (!repeats) {
if (isNaN(mode)) return mode;
else return +mode;
} else return null;
}
This was my approach, I tried a "functional" style using reduce. It also supports multimodes, so it will return an array of modes.
export function mode(vector) {
if (vector.length === 0) return undefined
return (vector.reduce((accu, curr) => {
const freqsMap = accu.freqsMap
freqsMap.set(curr, (freqsMap.get(curr) || 0) + 1)
const maxCount = freqsMap.get(curr) > accu.maxCount
? freqsMap.get(curr)
: accu.maxCount
const modes = freqsMap.get(curr) === accu.maxCount
? [...accu.modes, curr]
: freqsMap.get(curr) > accu.maxCount
? [curr]
: accu.modes
return { freqsMap, maxCount, modes }
}, { freqsMap: new Map(), maxCount: 1, modes: []})).modes
}
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Delete from array in javascript
I have the following JSON object:
[id:84,id:92,id:123,id:2353]
How would I go about removing the item which the value is "123" using javascript?
or if I formatted the json as
[84, 92, 123, 2353]
How would it be removed in this case?
Assume you have this:
var items = [{ id: 84 }, { id: 92 }, { id: 123 }, { id: 2353 }];
var filtered = items.filter(function(item) {
return item.id !== 123;
});
//filtered => [{ id: 84 }, { id: 92 }, { id: 2353 }]
Supposing you actually have an object from a json in the json variable
for (key in json) {
if (json.hasOwnProperty(key) && json[key] == 123) {
delete json[key];
}
}
Shorter alternative would be:
var newArr = [{id:84}, {id:92}, {id:123}, {id:2353}].filter(function(a) {
return a.id != 123;
});
If you have this:
var arr = [{id:84}, {id:92}, {id:123}, {id:2353}]
To remove the item with value 123, you can do:
for(var i = 0; i < arr.length; i++) {
if(arr[i].id == 123) {
arr.splice(i, 1);
break;
}
}
function removeClass(obj, cls) {
var classes = obj.className.split(' ');
for(i=0; i<classes.length; i++) {
if (classes[i] == cls) {
classes.splice(i, 1);
i--; // (*)
}
}
obj.className = classes.join(' ');
}
var obj = { className: 'open menu menu' }
removeClass(obj, 'menu')
alert(obj.className)
You can use splice function, like this:
var data = [{id:84}, {id:92}, {id:123}, {id:2353}];
function remove(){
for(var i = 0, max = data.length; i < max; i++) {
var a = data[i];
if(a.id === 123) {
data.splice(i, 1);
break;
}
}
}
remove();
Seems like you want to avoid a loop. Assuming it's available, you can use .filter:
[{id:84},{id:92},{id:123},{id:2353}]
.filter(function (elem) { return elem.id !== 123; });
This technically does do a loop, but at least you don't have to look at it.
Assuming your "json" is really an array, like [84, 92, 123, 2353]:
var myString = "[84, 92, 123, 2353]";
var myArray = JSON.parse(myString);
var index = myArray.indexOf(123); // whatever value you are looking for
myArray.splice(index, 1);
http://jsfiddle.net/7vkK6/
Assuming I'm understanding your question and comments correctly you can do something like this:
var old_array = [{id: 84},...];
var new_array = [];
for(var i = 0, len = old_array.length; i++) {
if (old_array[i].id != 123) new_array.push(old_array[i]);
}
What you have currently is not JSON so I'll give you some different options.
If you have an Array arr = [84,92,123,2353] then
arr = arr.filter(function (x) {return x !== 123;}); // all occurrences
// OR
arr.splice(arr.indexOf(123), 1); // first occurrence only
If you have an Object obj = {"84": a, "92": b, "123": c, "2353": d}, a to d some expressions, then
delete obj['123']; // obj now {"84": a, "92": b, "2353": d}
1) JSON is a string, not an array or an object.
var json = "[1,2,3]";
2) Valid JSON NEEDS to be valid JS
var myJSObj = { 1,2,3 }, // broken
myJSArr = [ name : 1, name2 : 2 ]; // broken
3) If you have a JS Array, you can remove an element by using [].splice
var arr = [ 1, 2, 3, 4 ],
i = 0, l = arr.length,
test = 4;
for (; i < l; i += 1) {
if (arr[i] === test) { arr.splice(i, 1); } // remove 1 starting at i
}
4) If you have an object with named keys, you can use delete
var obj = { val : 1 };
delete obj.val;