I am learning JS, and I have homework. I am asked to transform array into new array where each item is represented by the running count of element appearances.
For example
[1, 2, 1, 1, 3]
becomes
[1, 1, 2, 3, 1]
I wrote a code which works for numbers, but fails tests with strings:
UPDATE: IT works for some numbers, for others does not :/
function duplicates(arr) {
let i, j, newArr = [],
count = 1;
for (i = 0; i < arr.length; i++) {
for (j = 0; j < arr.length; j++) {
if (i == j) {
continue
}
if (arr[i] === arr[j]) {
newArr.push(count++)
break
}
}
if (j === arr.length) {
newArr.push(1)
}
}
return newArr
}
console.log(duplicates(['a', 'a', 'aa', 'a', 'aa'])) //[ 1, 2, 1, 3, 2] <-- FAILS
console.log(duplicates([1, 2, 1, 2, 3, 1])) //[1, 2, 3, 4, 1, 5] <-- fails
console.log(duplicates([1, 2, 1, 1, 3])) //[ 1, 1, 2, 3, 2, 1 ] <-- MY CODE WORKS
Can you give me a hint? :/
Thank you!
One approach is to use .map(), .slice() and .filter()
const duplicates = (nums) =>
nums.map((value, index) => {
const segment = nums.slice(0,index+1);
return segment.filter(v => v === value).length;
});
console.log(duplicates([1, 2, 1, 1, 3]));
console.log(duplicates([1, 2, 1, 2, 3, 1]));
console.log(duplicates(['a', 'a', 'aa', 'a', 'aa']));
map creates a new array by iterating through nums and transforming each value via a function
slice is used to create a new array based on nums. In first example, the new array is [1] in first iteration, [1,2] in second, followed by [1,2,1] and so on.
filter finds the items in the array from #2 that match the current value.
Elaborating on #CRice and #Kaiido idea, let's create an object that is creating the count of the items while you're looping through the array:
function duplicates(arr) {
const obj = {};
let value = 0;
let newArr = [];
for (i = 0; i < arr.length; i++) {
value = arr[i];
if (obj[value]){
obj[value] = obj[value] + 1;
}
else{
obj[value] = 1;
}
newArr.push(obj[value]);
}
return newArr
}
console.log(duplicates(['a', 'a', 'aa', 'a', 'aa'])) //[ 1, 2, 1, 3, 2] <-- FAILS
console.log(duplicates([1, 2, 1, 2, 3, 1])) //[1, 2, 3, 4, 1, 5] <-- fails
console.log(duplicates([1, 2, 1, 1, 3])) //[ 1, 1, 2, 3, 2, 1 ] <-- MY CODE WORKS
JS has a nice built-in, reduce, that does so in a simpler way:
const duplicates = (arr) => {
const obj = {}
return arr.reduce ( (acc,cur) => {
obj[cur] = (obj[cur])?obj[cur]+1:1
acc.push(obj[cur])
return acc
}, []);
}
console.log(duplicates(['a', 'a', 'aa', 'a', 'aa'])) //[ 1, 2, 1, 3, 2] <-- FAILS
console.log(duplicates([1, 2, 1, 2, 3, 1])) //[1, 2, 3, 4, 1, 5] <-- fails
console.log(duplicates([1, 2, 1, 1, 3])) //[ 1, 1, 2, 3, 2, 1 ] <-- MY CODE WORKS
Related
i want to specify how many 1 elements I want to remove within that array:
const array = [1, 1, 2, 3, 1, 5];
I tried like that:
const array = [1, 1, 2, 3, 1, 5];
const i = array.indexOf(1);
if (i > - 1) {
array.splice(i, 1);
}
but this remove just first element 1 in array
Try this, Array.prototype.filter will create a new array and will not mutate or change the array on which it is called.
const arr = [1, 1, 2, 3, 1, 5];
const newArr = arr.filter((item) => item !== 1);
console.log(newArr);
const removeArrayItem = (array, item) => array.filter((i) => i !== item);
console.log(removeArrayItem([1, 2, 2, 2, 2, 3, 4, 5], 2));
console.log(removeArrayItem([1, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8, 8, 8], 8));
// or using reduce
const removeDuplicateItem = (arr, itemToRemove, count) =>
arr.reduce(
(accumulator, currentItem) => {
if (
accumulator.count &&
accumulator.count > 0 &&
currentItem === itemToRemove
) {
accumulator.count -= 1;
return accumulator;
}
accumulator.result.push(currentItem);
return accumulator;
},
{
count,
result: [],
},
).result;
// This will remove 5 number twos
console.log(removeDuplicateItem([1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8, 8, 8], 2, 5));
You need to iterate the count and check if an item is available for removing.
function remove(array, value, count) {
while (count--) {
const i = array.indexOf(value);
if (i === -1) break;
array.splice(i, 1);
}
}
const array = [1, 1, 2, 3, 1, 5];
remove(array, 1, 2);
console.log(array);
If you want to remove only the amount of 1s you specify,
You can use a for loop with 2 conditions,
one for array size and the second for the number of times 1 was found and removed.
const array = [1, 1, 2, 3, 1, 5];
const counter = 2;
for(let i = 0 , n = 0; i < array.length && n < counter; i++) {
if(array[i] === 1) {
array.splice(i, 1);
i -= 1;
n++;
}
}
console.log(array);
You can use a while loop to remove N number of elements with this value:
const removeNInstancesOfX = (array, n, x) => {
let i = null, count = 0, arr = [...array];
while ((i = arr.indexOf(1)) !== -1 && count++ < n)
arr.splice(i, 1);
return arr;
}
let array = [1, 1, 2, 3, 1, 5];
array = removeNInstancesOfX(array, 2, 1);
console.log(...array);
I have two arrays:
arr1 = [1, 2, 3, 1, 2, 3, 4]
arr2 = [1, 3, 1, 1]
arr3 = [1, 1, 2, 2, 3]
Using arr1 as the baseline, arr2 does not match because it contains three 1's whereas arr1 only has two. arr3, however should return true because it has elements from arr1.
I tried
if(_.difference(arr2, arr1).length === 0)
But this does not take into account the number of occurrences
You could count all value from the first array and iterate the second with every and return early if a value is not given or zero, otherwise decrement the count and go on.
function check(a, b) {
var count = a.reduce((o, v) => (o[v] = (o[v] || 0) + 1, o), {});
return b.every(v => count[v] && count[v]--);
}
var arr1 = [1, 2, 3, 1, 2, 3, 4],
arr2 = [1, 3, 1, 1],
arr3 = [1, 1, 2, 2, 3];
console.log(check(arr1, arr2)); // false
console.log(check(arr1, arr3)); // true
You can try to loop through second array and compare t against main array if value found make main array cell to false and set flag as true
arr1 = [1, 2, 3, 1, 2, 3, 4]
arr2 = [1, 3, 1, 1]
arr3 = [1, 1, 2, 2, 3]
function checkArray(compareMe, toThis){
var flag = false;
for(var i = 0; i < toThis.length; i++){
flag = false;
for(var j = 0; j < compareMe.length; j++){
if(compareMe[j] == toThis[i]){
compareMe[j] = false;
flag = true;
break;
}
}
}
return flag;
}
console.log(checkArray(arr1, arr2));
console.log(checkArray(arr1, arr3));
Try this solution:
const arr1 = [1, 2, 3, 1, 2, 3, 4],
arr2 = [1, 3, 1, 1],
arr3 = [1, 1, 2, 2, 3],
arr4 = [1, 2, 3, 1, 2, 3, 4, 1];
function containsFn(src, trg) {
const srcCp = src.slice();
for(let i = 0; i < trg.length; i++) {
const index = srcCp.indexOf(trg[i]);
if(index > - 1) {
srcCp.splice(index, 1);
} else {
return false;
}
}
return true;
}
console.log(containsFn(arr1, arr2));
console.log(containsFn(arr1, arr3));
console.log(containsFn(arr1, arr4));
Looks like I'm already late to the party but this would be a recursive solution:
arr1 = [1, 2, 3, 1, 2, 3, 4]
arr2 = [1, 3, 1, 1]
arr3 = [1, 1, 2, 2, 3]
function findAllIn(find, search) {
if (find.length == 0) {
return true;
}
i = search.indexOf(find[0]);
if (i == -1) {
return false;
}
return findAllIn(find.slice(1,find.length), search.slice(0,i).concat(search.slice(i+1, search.length)));
}
console.log(findAllIn(arr2, arr1)); // false
console.log(findAllIn(arr3, arr1)); // true
This should do the trick
Not efficient but I think it is easy to understand
const count = (x, xs) =>
xs.reduce((y, xi) => y + (xi === x ? 1 : 0), 0)
const isInclusive = (xs, ys) =>
xs.every((xi) => count(xi, xs) >= count(xi, ys))
const arr1 = [1, 2, 3, 1, 2, 3, 4]
const arr2 = [1, 3, 1, 1]
const arr3 = [1, 1, 2, 2, 3]
console.log(isInclusive(arr1, arr2))
console.log(isInclusive(arr1, arr3))
Based on this answer: https://stackoverflow.com/a/4026828/3838031
You can do this:
Array.prototype.diff = function(a) {
return this.filter(function(i) {return a.indexOf(i) < 0;});
};
arr1 = [1, 2, 3, 1, 2, 3, 4]
arr2 = [1, 3, 1, 1]
arr3 = [1, 1, 2, 2, 3]
console.log((arr1.diff(arr2).length === 0) ? "True" : "False");
console.log((arr1.diff(arr3).length === 0) ? "True" : "False");
console.log((arr2.diff(arr1).length === 0) ? "True" : "False");
console.log((arr2.diff(arr3).length === 0) ? "True" : "False");
console.log((arr3.diff(arr1).length === 0) ? "True" : "False");
console.log((arr3.diff(arr2).length === 0) ? "True" : "False");
I couldn't find an answer to this specific question on S.O.
Let's say I have an array of strings, or in this case, numbers:
var x = [1, 1, 1, 2, 3, 3, 5, 3, 3, 5, 4, 5];
I'd like the output to be:
var output = [[1,1,1], [2], [3,3,3,3,3], [4], [5, 5, 5]];
I was hoping to use Lodash but most of that stuff tends to remove duplicates rather chunk them together into their own array. Maybe some kind of .map iterator?
The order of the output doesn't really matter so much. It just needs to chunk the duplicates into separate arrays that I'd like to keep.
You can use reduce to group the array elements into an object. Use Object.values to convert the object into an array.
var x = [1, 1, 1, 2, 3, 3, 5, 3, 3, 5, 4, 5];
var result = Object.values(x.reduce((c, v) => {
(c[v] = c[v] || []).push(v);
return c;
}, {}));
console.log(result);
Shorter version:
var x = [1, 1, 1, 2, 3, 3, 5, 3, 3, 5, 4, 5];
var result = Object.values(x.reduce((c, v) => ((c[v] = c[v] || []).push(v), c), {}));
console.log(result);
You can do this with Array.reduce in a concise way like this:
var x = [1, 1, 1, 2, 3, 3, 5, 3, 3, 5, 4, 5]
let result = x.reduce((r,c) => (r[c] = [...(r[c] || []), c],r), {})
console.log(Object.values(result))
The exact same with lodash would be:
var x = [1, 1, 1, 2, 3, 3, 5, 3, 3, 5, 4, 5]
let result = _.values(_.groupBy(x))
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
Using _.values to extract the values of the grouping object and _.groupBy to get the actual groupings
Use Array#prototype#reduce to group them:
const x = [1, 1, 1, 2, 3, 3, 5, 3, 3, 5, 4, 5];
let helperObj = {};
const res = x.reduce((acc, curr) => {
// If key is not present then add it
if (!helperObj[curr]) {
helperObj[curr] = curr;
acc.push([curr]);
}
// Else find the index and push it
else {
let index = acc.findIndex(x => x[0] === curr);
if (index !== -1) {
acc[index].push(curr);
}
}
return acc;
}, []);
console.log(res);
Since you're hoping to use Lodash, you might be interested in groupBy. It returns on object, but the _.values will give you the nested array:
var x = [1, 1, 1, 2, 3, 3, 5, 3, 3, 5, 4, 5];
let groups = _.values(_.groupBy(x))
console.log(groups)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
Here's an imperative solution:
var x = [1, 1, 1, 2, 3, 3, 5, 3, 3, 5, 4, 5];
x.sort();
var res = [];
for (const [i, n] of x.entries()) {
if (n !== x[i-1]) res.push([n]);
else res[res.length-1].push(n);
}
console.log(res);
I have a multidimensional array. I want to group the values in this and know how many.
I've created a new array. I've looped a multidimensional array. If the current value does not exist in the new array, I add this value into the array. But I couldn't do it dynamically, they were all added to the bottom. I couldn't add it to "subCategories".
In this way I have a multidimensional array.
currentArray = [
[1, 2, 3, 5],
[1, 2, 3, 4],
[1, 2, 3, 4],
[1, 2, 3, 4],
[1, 2, 3, 4],
[1, 2, 3, 4],
[1, 2, 3, 4]
]
I used a loop like this.
newArray= [];
for (let i = 0; i < currentArray.length; i++) {
for (let k = 0; k < currentArray[i].length; k++) {
let obj = { id: currentArray[i][k], subCategories: [] };
let index = newCategories.findIndex(x => x.id === obj.id);
if (index === -1) {
if (k === 0) {
newCategories.push(obj);
}
} else {
newCategories[index].subCategories.push(obj);
}
}
}
I used a loop like this but I did not get a successful result. Logic error in the current code and I couldn't figure it out.
I want the same elements in the array to be added to the new array only once. And I want to get "count" in the last elements.
So the output I want to achieve is as follows.
{
"id": 1,
"subCategories": [
{
"id": 2,
"subCategories": [
{
"id": 3,
"subCategories": [
{
"id": 5,
"count": 1,
"subCategories": []
},
{
"id": 4,
"count": 6,
"subCategories": []
}
]
}
]
}
]
}
You could reduce the array by reduceing the inner array and look for the wanted id.
var array = [[1, 2, 3, 5], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]],
result = array
.reduce((r, a) => {
var o = a.reduce((p, id) => {
var temp = p.subCategories.find(q => q.id === id);
if (!temp) {
p.subCategories.push(temp = { id, subCategories: [] });
}
return temp;
}, r);
o.count = (o.count || 0) + 1;
return r;
}, { subCategories: [] })
.subCategories;
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
This is in the same style as you had, by using a starting object which matches the inner format and a search for the items for returning this object for next level.
var currentArray = [[1, 2, 3, 5], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]],
newArray = [],
temp,
item;
for (let i = 0; i < currentArray.length; i++) {
temp = { subCategories: newArray };
for (let k = 0; k < currentArray[i].length; k++) {
item = temp.subCategories.find(x => x.id === currentArray[i][k]);
if (!item) {
temp.subCategories.push(item = { id: currentArray[i][k], subCategories: [] });
}
temp = item;
}
temp.count = (item.count || 0) + 1;
}
console.log(newArray);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I want to map a sequence to another sequence such as
map (0,1,2,3,4) + (1,2,3,4,5) -> (1,3,5,7,9)
How to do that in lazy.js or underscore ?
Thanks!
You can use _.zip and _.map, like this
var _ = require("underscore");
function sum(numbers) {
var result = 0;
for (var i = 0; i < numbers.length; i += 1) {
result += numbers[i];
}
return result;
}
console.log(_.map(_.zip([0, 1, 2, 3, 4], [1, 2, 3, 4, 5]), sum))
// [ 1, 3, 5, 7, 9 ]
Since only two numbers are going to be there, always, you can simplify that like this
var result = _.chain([0, 1, 2, 3, 4])
.zip([1, 2, 3, 4, 5])
.map(function(numbers) {
return numbers[0] + numbers[1];
})
.value();
console.log(result);
You can make it a little more generic and clean, like this
function sum(numbers) {
return numbers.reduce(function(result, current) {
return result + current;
}, 0);
}
var result = _.chain([0, 1, 2, 3, 4])
.zip([1, 2, 3, 4, 5])
.map(sum)
.value();
Or even simpler, like in the first answer
console.log(_.map(_.zip([0, 1, 2, 3, 4], [1, 2, 3, 4, 5]), sum));
Using underscore, #thefortheye's solution works well, here's a similar solution just using lazy.js instead;
> var Lazy = require('lazy.js');
> var addParts = function(x) { return Lazy(x).sum(); }
> Lazy([0,1,2,3,4]).zip([1,2,3,4,5]).map(addParts).toArray()
[ 1, 3, 5, 7, 9 ]
The above example can be achieved in the following way using underscore:
_.map([0, 1, 2, 3, 4], function(n, i) {
return n + i + 1
}) // This returns [1, 3, 5, 7, 9]
Here's a link to the API docs: Underscore#map