If I call the filter function, I get this array returned [ 1, , 3, , 5 ]. From where come the additional commas? I don't understand this effect. Can somebody explain it to me?
The array should be that: [ 1, 3, 5 ].
class List {
constructor(values = []) {
this._list = values;
}
filter(func) {
let newList = new Array();
let indexList = 0;
let indexNewList = 0;
while (this._list[indexList] != undefined) {
if (func(this._list[indexList]) === true) {
newList[indexNewList] = this._list[indexList];
indexNewList++;
}
indexList++;
}
this._list = newList;
return this;
}
get values() { return this._list }
}
var isOdd = function (x) {
return x % 2 === 1;
};
var list = new List([1, 2, 3, 4, 5]);
console.log(list.filter(isOdd).values);
If an item in the list matches the filter, you're inserting it into the new list at the index of the item in the original list. You want to simply be appending the item to the new list.
Use another variable to keep track of what index the element should be inserted into the new list at:
let newList = new Array();
let indexList = 0;
let newIndex = 0;
while (this._list[indexList] != undefined) {
if (func(this._list[indexList]) === true) {
newList[newIndex] = this._list[indexList];
newIndex++;
}
indexList++;
}
The newIndex variable will only be incremented when an item has been inserted into newList, instead of being incremented with every iteration of the loop.
The problem is the increment of the variable index, that increment is creating empty/undefined elements.
For example:
Array = [1];
index = 1
callback returns false
The index is incremented by 1 -> index =2`
Next iteration callback returns true
A new element is added to Array at position 2 ->
Array = [1, undefined, 3].
Use a separated index for the newArray.
class List {
constructor(values = []) {
this._list = values;
}
filter(func) {
let newList = new Array();
let index = 0;
let newListIndex = 0;
while (this._list[index] != undefined) {
if (func(this._list[index]) === true) {
newList[newListIndex++] = (this._list[index]);
}
index++;
}
this._list = newList;
return this;
}
get values() {
return this._list
}
}
var isOdd = function(x) {
return x % 2 === 1;
};
var list = new List([1, 2, 3, 4, 5]);
console.log(list.filter(isOdd));
I would suggest sticking with a functional-programming style definition of array#filter since that is the paradigm it originates from.
This is the fully immutable version of List#filter where you get a new instance of List back and the underlying array never goes through any form of mutation.
class List {
constructor(values = []) {
this._list = values;
}
filter(func) {
var reduce = function(values, accum) {
if(values.length === 0) return accum;
if(func(values[0])) return reduce(values.slice(1), accum.concat(values[0]));
else return reduce(values.slice(1), accum)
}
return new List(reduce(this._list, []))
}
get values() {
return this._list
}
}
var isOdd = function(x) {
return x % 2 === 1;
};
var list = new List([1, 2, 3, 4, 5]);
console.log(list.filter(isOdd));
Related
In the below code:
First I create an array of 10 elements
Next I remove 3 random elements from that array.
I try to add those 3 random elements into the original array in the original positions.
For some reason in step 3 above (in the FINAL ARR console log), the elements are being added in the wrong position. Is my slice function incorrect in step 3? If so, how do I modify it to be correct?
// Step 1. Create array of 10 elements
const originalValues = maxAmount => {
let finalArray = [];
for (let i = 0; i < maxAmount; i++) {
finalArray.push({
key1: `key1-${i+1}`,
position: i
});
}
return finalArray;
}
let finalArr = originalValues(10);
const finalArrOriginalLength = finalArr.length;
//Step 2. Remove 3 random elements in this case key1-2, key1-7, key1-8
const removedElements = [];
finalArr = finalArr.filter(elem => {
if (elem.key1 === 'key1-7' || elem.key1 === 'key1-2' || elem.key1 === 'key1-8') {
removedElements.push(elem);
return false;
} else {
return true;
}
});
/* Step 3. Add those 3 elements back to the original positions */
removedElements.forEach(elem => {
finalArr.splice(elem.position - (finalArrOriginalLength - finalArr.length) + 1, 0, elem);
});
/* The inserted elements from step 3 are being inserted in the wrong position as seen when this console.log prints out finalArr */
console.log('FINAL ARR', finalArr);
There's no need to subtract from elem.position.
If you sort the removed elements by the original positions, you should be able to insert them at those positions.
This assumes there haven't been any other changes to the array.
// Step 1. Create array of 10 elements
const originalValues = maxAmount => {
let finalArray = [];
for (let i = 0; i < maxAmount; i++) {
finalArray.push({
key1: `key1-${i+1}`,
position: i
});
}
return finalArray;
}
let finalArr = originalValues(10);
const finalArrOriginalLength = finalArr.length;
//Step 2. Remove 3 random elements in this case key1-2, key1-7, key1-8
const removedElements = [];
finalArr = finalArr.filter(elem => {
if (elem.key1 === 'key1-7' || elem.key1 === 'key1-2' || elem.key1 === 'key1-8') {
removedElements.push(elem);
return false;
} else {
return true;
}
});
/* Step 3. Add those 3 elements back to the original positions */
removedElements.sort((a, b) => a.position - b.position).forEach(elem => {
finalArr.splice(elem.position, 0, elem);
});
console.log('FINAL ARR', finalArr);
Try this...
// Step 1. Create array of 10 elements
const originalValues = maxAmount => {
let finalArray = [];
for (let i = 0; i < maxAmount; i++) {
finalArray.push({
key1: `key1-${i+1}`,
position: i
});
}
return finalArray;
}
let finalArr = originalValues(10);
const finalArrOriginalLength = finalArr.length;
//Step 2. Remove 3 random elements in this case key1-2, key1-7, key1-8
const removedElements = [];
finalArr = finalArr.filter(elem => {
if (elem.key1 === 'key1-7' || elem.key1 === 'key1-2' || elem.key1 === 'key1-8') {
removedElements.push(elem);
return false;
} else {
return true;
}
});
// Step 3. Add those 3 elements back to the original positions
removedElements
.sort((a,b)=>{return a.position > b.position ? 0 : 1 });
removedElements.forEach(elem => {
finalArr.splice(elem.position, 0, elem);
});
/* The inserted elements from step 3 are being inserted in the wrong position as seen when this console.log prints out finalArr */
console.log('FINAL ARR', finalArr);
I would like to browse an associative array like a circular list.
First the associative array is defined like this :
array = {item1:array(...), item2:array(...), ...}
When at the first element I browse the array of this element, once arrive at the last element of this array it should passe to the second element and brows it's array, and the same for the last one who must return to the first element.
so I initialize my array as follows:
// Build the associative array
Prot.prototype.additem = function(itemName, itemArray)
{
this.array[itemName] = itemArray; // itemArray is an array
}
// Init the currentItem of the associative array to browse (We don't necessarily start at the first)
Prot.prototype.init = function(itemName)
{
this.currentItem = this.array[itemName];
this.currentItemArray = 0;
}
Prot.prototype.next = function()
{
// here I browse the first array of the first key of my associative array
var index = this.currentItem.indexOf(this.currentItemArray);
index = index +1;
this.currentItemArray = this.currentItem[index];
if (index == (this.currentItemArray.length - 1))
{
// when arrives at the last element of the array of the first key I should pass to the second
return false;
}
else {
return true;
}
}
// I add a set interval at the end so no need for a loop
You'll need an array to know what is the "next" item array. So I would suggest storing the desired order in another array, having just those names.
Here is a possible implementation:
class Prot {
constructor() {
this.itemNames = [];
this.arrays = {};
this.hasData = false;
this.currentIndex = 0;
}
additem(itemName, itemArray) {
if (itemName in this.arrays) throw "duplicate entry";
this.arrays[itemName] = { data: itemArray, index: this.itemNames.length };
this.itemNames.push(itemName); // keep the order
if (itemArray.length) this.hasData = true;
}
init(itemName) {
this.currentItem = this.arrays[itemName];
this.currentIndex = 0;
}
next() {
if (!this.hasData) return;
if (!this.currentItem) this.currentItem = this.arrays[this.itemNames[0]];
var data = this.currentItem.data[this.currentIndex++];
while (this.currentIndex >= this.currentItem.data.length) {
this.currentItem = this.arrays[this.itemNames[(this.currentItem.index+1) % this.itemNames.length]];
this.currentIndex = 0;
}
return data;
}
}
// demo
let obj = new Prot;
// add the arrays:
obj.additem("a", [1, 2, 3]);
obj.additem("b", [4, 5]);
obj.additem("c", [6, 7, 8, 9]);
obj.additem("d", [0]);
// Start at "b":
obj.init("b");
// iterate from there...
for (let i = 0; i < 12; i++) {
console.log(obj.next());
}
There is no such thing as an associative array in JavaScript, but you can use an object instead. A very simple implementation of defining an object and referencing its properties in a circular way would be the following:
// define the object with 6 properties and assiociated values:
var obj={a:123, b:456, c:789, d:666, e:777, f:888};
function getcirc(obj){
// use a "static variable" inside the function:
if(typeof getcirc.i=="undefined") getcirc.i=0;
var keys=Object.keys(obj), k=keys[getcirc.i++%keys.length];
console.log(k,obj[k]);
}
// call the function repeatedly ...
for (var n=0;n<20;n++) getcirc(obj);
The problem is to find the unique number in a array such as [2,2,2,5].
The output should be 5 as it is the 1 unique element in the array.
I have attempted this:
function findUniq(arr) {
var b= arr[0];
var c;
for(var i=0; i<arr.length; i++)
{
if(arr[i]===b )
{
b=arr[i]
}
else
{
c=arr[i];
}
}
return c
console.log(findUniq([3, 5, 3, 3, 3]))
This works fine unless the unique number is the first element in the array. How do I fix this?
You can use indexOf and lastIndexOf to see if a value occurs more than once in the array (if it does, they will be different), and if so, it is not the unique value. Use filter to process the array:
let array = [2,2,2,5];
console.log(array.filter(v => array.indexOf(v) === array.lastIndexOf(v)));
array = [5,3,3,3,3];
console.log(array.filter(v => array.indexOf(v) === array.lastIndexOf(v)));
array = [4,4,5,4];
console.log(array.filter(v => array.indexOf(v) === array.lastIndexOf(v)));
You can create a recursive function that will take the first element of the array and see if it exists in the rest of it, if it does, it will take the next element and do the same, return the element if it doesn't exist in the rest of the array :
const arr = [3, 3, 3, 5, 3];
const find = arr => {
const [f, ...rest] = arr;
if(rest.includes(f))
return find(rest);
else
return f;
}
const result = find(arr);
console.log(result);
Note that this will return the last element if all of them are the same [3,3,3] will return 3
Try something like this using a set, which only stores unique elements:
var set = new Set(arr);
// count instances of each element in set
result = {};
for(var i = 0; i < a.length; ++i) {
if(!result[arr[i]])
result[arr[i]] = 0;
++result[arr[i]];
}
for (var value in result) {
if (value == 1) {
return value;
}
}
// if there isn't any
return false;
This should work, please tell me if it doesn't.
This is another implementation that is surely less efficient than that of #Nick's, but it is a valid algorithm anyway:
function findUniq(arr) {
var elemCount = new Map();
var uniq = [];
// Initialize elements conts
for (var k of arr.values()) {
elemCount.set(k, 0);
}
// Count elements
for (var k of arr.values()) {
elemCount.set(k, elemCount.get(k) + 1);
}
// Add uniq elements to array
for (var [k, v] of elemCount.entries()) {
if (v === 1) uniq.push(k);
}
return uniq;
}
console.log(findUniq([3, 5, 3, 3, 3]))
if you prefer .reduce over .map for your use case (for performance/etc. reasons):
function existance(data) {
return data.reduce((a, c) => (data.indexOf(c) === data.lastIndexOf(c)) ? a.concat(c) : a, []);
}
console.log(existance([1,1,1,2]));
console.log(existance([1,1,2,3,4,5,5,6,6,6]));
Say my function is called as,
my_function(1, 2, 5, null, 4, null, 1, 3);
and I want to split it into separate segments that last up until a null.
Here's what I have so far:
my_function = (...theArgs) => {
let _theArgs = [];
let _tempArray = [];
let j = 0;
for (let i = 0; i < theArgs.length; i++) {
if (theArgs[i] != null) {
_tempArray[j].push(theArgs[i]); //breaks here
} else {
_theArgs[j].push(_tempArray); //and here
_tempArray = [];
j++;
}
}
return _theArgs;
}
my_function(1, 2, 5, null, 4, null, 1, 3);
So here I am trying to cycle through each argument passed to the function and split it up into a 2D array. For instance, my_function(1, 2, 5, null, 4, null, 1, 3); would return an array _theArgs where _theArgs[0] = [1, 2, 5], _theArgs[1] = [4] and _theArgs[2] = [1, 3]
I've indicated where my code breaks.. any suggestion to approaching this would be much appreciated
You could search for null and push the parts to the result array.
function fn(...args) {
var result = [],
r = 0,
l;
while ((l = args.indexOf(null, l)) !== -1) {
result.push(args.slice(r, l));
r = l + 1;
l += 2;
}
result.push(args.slice(r));
return result;
}
console.log(fn(1, 2, 5, null, 4, null, 1, 3));
_tempArray[j].push() fails because _tempArray[j] is not an array. _tempArray is initially an empty array, there's nothing in _tempArray[j], so you can't push onto it. I think you just want _tempArray.push(theArgs[i]).
The same with _theArgs[j].
You also need to push onto _theArgs at the end of the function, to get arguments after the last null.
my_function = (...theArgs) => {
let _theArgs = [];
let _tempArray = [];
for (let i = 0; i < theArgs.length; i++) {
if (theArgs[i] !== null) {
_tempArray.push(theArgs[i]);
} else {
_theArgs.push(_tempArray);
_tempArray = [];
}
}
if (_tempArray.length > 0) {
_theArgs.push(_tempArray);
}
return _theArgs;
}
console.log(my_function(1, 2, 5, null, 4, null, 1, 3));
This is what you need: (Since you are pushing you don't need the variable j)
my_function = (...theArgs) => {
let _theArgs = [];
let _tempArray = [];
for (let i = 0; i < theArgs.length; i++) {
if (theArgs[i] != null) {
_tempArray.push(theArgs[i]); //breaks here
} else {
_theArgs.push(_tempArray); //and here
_tempArray = [];
}
}
return _theArgs;
}
console.log(my_function(1, 2, 5, null, 4, null, 1, 3));
It looks like you're trying to call push() on an undefined nested array object (that is being accessed via the j variable).
Consider making the following changes:
const my_function = (...theArgs) => {
let _theArgs = [];
let _tempArray = [];
for (let i = 0; i < theArgs.length; i++) {
if (theArgs[i] != null) {
// Push to _tempArray directly. No need to access via "j"
_tempArray.push(theArgs[i]);
} else {
// Push to _theArgs directly. No need to access via "j"
_theArgs.push(_tempArray);
_tempArray = [];
}
}
// Include last args items (if any after final null)
if(_tempArray.length > 0) {
_theArgs.push(_tempArray);
}
return _theArgs;
}
const result = my_function(1, 2, 5, null, 4, null, 1, 3);
console.log(result)
The j index is throwing you off; remove it in both cases (it's an undefined offset; push will create the new element automatically).
Also, you'll need to append the final _tempArray before returning the result to ensure the last row is added.
Here's a cleaner version which avoids indexes entirely:
const myFunction = (...args) => {
let row = [];
const result = args.reduce((a, e) => {
if (e === null) {
a.push(row);
row = [];
}
else {
row.push(e);
}
return a;
}, []);
return row.length ? result.concat([row]) : result;
}
console.log(myFunction(1, 2, 5, null, 4, null, 1, 3));
Another Solution:
function my_function(...args) {
var i = 0, temp = [], lastElt = null;
for (j = 0; j < args.length; j++) {
if (args[j] !== null) {
if (temp[i] === undefined) {
temp[i] = [];
}
temp[i].push(args[j]);
} else if (lastElt !== null) {
i++;
}
lastElt = args[j];
}
return temp;
}
var test = my_function(null, 1, 2, 5, null, null, 4, null, null, null, 1, 3);
console.log(test);
As covered in earlier answers the problem is caused by processing a second dimension of _theArgs and _tempArray when only one (or the first) dimension should be used.
Removing two occurences of [j] and the j++; line will fix this but needs patch-up code to include an array of arguments that follow the last null argument.
You could use Array.prototype.reduce to avoid explicit loop code and patchups as may be required:
const args2D = (...argList) =>
argList.reduce(
(result, arg) => {
if (arg === null){
result.push( [] );
}
else {
result[ result.length-1].push( arg);
}
return result;
},
[[]]
);
console.log( args2D(1, 2, 5, null, 4, null, 1, 3));
This approach always produces a two dimensional array, with empty inner array(s) if the argument list is empty, or no non-null values follow a null value in the list.
Short and Sweet - not recommended for server-side scripts because its not as performant as a for loop solution but for the client you may save some bandwidth. Also note that this solution, as presented, only works with numbers, nulls and modern browsers.
my_function = (...theArgs) => {
var _theArgs = theArgs.toString().split(',,');
for(arg in _theArgs) {
_theArgs[arg] = _theArgs[arg].split(',').map(function(v) {return +v })
}
return _theArgs;
};
console.log(my_function(1, 2, 5, null, 4, null, 1, 3) );
I have an array of arrays as follows:
[[3, 4], [1, 2], [3, 4]]
I wish to create a new array of arrays that has no duplicates, and has a count of the number of occurrences of each element in the first array:
[[3,4,2], [1,2,1]]
here is what I have so far:
var alreadyAdded = 0;
dataset.forEach(function(data) {
From = data[0];
To = data[1];
index = 0;
newDataSet.forEach(function(newdata) {
newFrom = newData[0];
newTo = newData[1];
// check if the point we are looking for is already added to the new array
if ((From == newFrom) && (To == newTo)) {
// if it is, increment the count for that pair
var count = newData[2];
var newCount = count + 1;
newDataSet[index] = [newFrom, newTo, newCount];
test = "reached here";
alreadyAdded = 1;
}
index++;
});
// the pair was not already added to the new dataset, add it
if (alreadyAdded == 0) {
newDataSet.push([From, To, 1]);
}
// reset alreadyAdded variable
alreadyAdded = 0;
});
I am very new to Javascript, can someone help explain to me what I'm doing wrong? I'm sure there is a more concise way of doing this, however I wasn't able to find an example in javascript that dealt with duplicate array of arrays.
Depending on how large the dataset is that you're iterating over I'd be cautious of looping over it so many times. You can avoid having to do that by creating an 'index' for each element in the original dataset and then using it to reference the elements in your grouping. This is the approach that I took when I solved the problem. You can see it here on jsfiddle. I used Array.prototype.reduce to create an object literal which contained the grouping of elements from the original dataset. Then I iterated over it's keys to create the final grouping.
var dataSet = [[3,4], [1,2], [3,4]],
grouping = [],
counts,
keys,
current;
counts = dataSet.reduce(function(acc, elem) {
var key = elem[0] + ':' + elem[1];
if (!acc.hasOwnProperty(key)) {
acc[key] = {elem: elem, count: 0}
}
acc[key].count += 1;
return acc;
}, {});
keys = Object.keys(counts);
for (var i = 0, l = keys.length; i < l; i++) {
current = counts[keys[i]];
current.elem.push(current.count);
grouping.push(current.elem);
}
console.log(grouping);
Assuming order of sub array items matters, assuming that your sub arrays could be of variable length and could contain items other than numbers, here is a fairly generic way to approach the problem. Requires ECMA5 compatibility as it stands, but would not be hard to make it work on ECMA3.
Javascript
// Create shortcuts for prototype methods
var toClass = Object.prototype.toString.call.bind(Object.prototype.toString),
aSlice = Array.prototype.slice.call.bind(Array.prototype.slice);
// A generic deepEqual defined by commonjs
// http://wiki.commonjs.org/wiki/Unit_Testing/1.0
function deepEqual(a, b) {
if (a === b) {
return true;
}
if (toClass(a) === '[object Date]' && toClass(b) === '[object Date]') {
return a.getTime() === b.getTime();
}
if (toClass(a) === '[object RegExp]' && toClass(b) === '[object RegExp]') {
return a.toString() === b.toString();
}
if (a && typeof a !== 'object' && b && typeof b !== 'object') {
return a == b;
}
if (a.prototype !== b.prototype) {
return false;
}
if (toClass(a) === '[object Arguments]') {
if (toClass(b) !== '[object Arguments]') {
return false;
}
return deepEqual(aSlice(a), aSlice(b));
}
var ka,
kb,
length,
index,
it;
try {
ka = Object.keys(a);
kb = Object.keys(b);
} catch (eDE) {
return false;
}
length = ka.length;
if (length !== kb.length) {
if (Array.isArray(a) && Array.isArray(b)) {
if (a.length !== b.length) {
return false;
}
} else {
return false;
}
} else {
ka.sort();
kb.sort();
for (index = 0; index < length; index += 1) {
if (ka[index] !== kb[index]) {
return false;
}
}
}
for (index = 0; index < length; index += 1) {
it = ka[index];
if (!deepEqual(a[it], b[it])) {
return false;
}
}
return true;
};
// Recursive function for counting arrays as specified
// a must be an array of arrays
// dupsArray is used to keep count when recursing
function countDups(a, dupsArray) {
dupsArray = Array.isArray(dupsArray) ? dupsArray : [];
var copy,
current,
count;
if (a.length) {
copy = a.slice();
current = copy.pop();
count = 1;
copy = copy.filter(function (item) {
var isEqual = deepEqual(current, item);
if (isEqual) {
count += 1;
}
return !isEqual;
});
current.push(count);
dupsArray.push(current);
if (copy.length) {
countDups(copy, dupsArray);
}
}
return dupsArray;
}
var x = [
[3, 4],
[1, 2],
[3, 4]
];
console.log(JSON.stringify(countDups(x)));
Output
[[3,4,2],[1,2,1]]
on jsFiddle
After fixing a typo I tried your solution in the debugger; it works!
Fixed the inner forEach-loop variable name to match case. Also some var-keywords added.
var alreadyAdded = 0;
dataset.forEach(function (data) {
var From = data[0];
var To = data[1];
var index = 0;
newDataSet.forEach(function (newData) {
var newFrom = newData[0];
var newTo = newData[1];
// check if the point we are looking for is already added to the new array
if ((From == newFrom) && (To == newTo)) {
// if it is, increment the count for that pair
var count = newData[2];
var newCount = count + 1;
newDataSet[index] = [newFrom, newTo, newCount];
test = "reached here";
alreadyAdded = 1;
}
index++;
});
// the pair was not already added to the new dataset, add it
if (alreadyAdded == 0) {
newDataSet.push([From, To, 1]);
}
// reset alreadyAdded variable
alreadyAdded = 0;
});
const x = [[3, 4], [1, 2], [3, 4]];
const with_duplicate_count = [
...x
.map(JSON.stringify)
.reduce( (acc, v) => acc.set(v, (acc.get(v) || 0) + 1), new Map() )
.entries()
].map(([k, v]) => JSON.parse(k).concat(v));
console.log(with_duplicate_count);