Javascript/lodash how to remove empty arrays - javascript

I'm working with this structure:
[
[
{
"comments":"asd",
"movement":"Back Squat",
"userID":"wDHZv3OL55SIymHkhMUejNleNkx1",
"weight":"330"
}
],
[
{
"comments":"asd",
"movement":"Bench Press",
"userID":"wDHZv3OL55SIymHkhMUejNleNkx1",
"weight":"100"
}
],
[
{
"comments":"Comment",
"movement":"Clean",
"userID":"wDHZv3OL55SIymHkhMUejNleNkx1",
"weight":"195"
}
],
[
],
[
],
[
{
"comments":"Front squat comment alpha",
"movement":"Front Squat",
"userID":"wDHZv3OL55SIymHkhMUejNleNkx1",
"weight":"315"
}
],
[
],
[
],
[
],
[
],
[
],
[
{
"comments":"abc",
"movement":"Strict Press",
"userID":"wDHZv3OL55SIymHkhMUejNleNkx1",
"weight":"155"
}
]
]
This is the input I'm using in JSON format. As you can see there are multiple empty arrays.
How would I go about filtering through these arrays and remove the empty ones?

Use the native Array#filter or lodash's _.filter(), and keep the sub arrays with length other than 0.
Array#filter
var arrs = [[{"comments":"asd","movement":"Back Squat","userID":"wDHZv3OL55SIymHkhMUejNleNkx1","weight":"330"}],[{"comments":"asd","movement":"Bench Press","userID":"wDHZv3OL55SIymHkhMUejNleNkx1","weight":"100"}],[{"comments":"Comment","movement":"Clean","userID":"wDHZv3OL55SIymHkhMUejNleNkx1","weight":"195"}],[],[],[{"comments":"Front squat comment alpha","movement":"Front Squat","userID":"wDHZv3OL55SIymHkhMUejNleNkx1","weight":"315"}],[],[],[],[],[],[{"comments":"abc","movement":"Strict Press","userID":"wDHZv3OL55SIymHkhMUejNleNkx1","weight":"155"}]];
var result = arrs.filter(function(sub) {
return sub.length;
});
console.log(result);
Lodash's _.filter() with _.size:
var arrs = [[{"comments":"asd","movement":"Back Squat","userID":"wDHZv3OL55SIymHkhMUejNleNkx1","weight":"330"}],[{"comments":"asd","movement":"Bench Press","userID":"wDHZv3OL55SIymHkhMUejNleNkx1","weight":"100"}],[{"comments":"Comment","movement":"Clean","userID":"wDHZv3OL55SIymHkhMUejNleNkx1","weight":"195"}],[],[],[{"comments":"Front squat comment alpha","movement":"Front Squat","userID":"wDHZv3OL55SIymHkhMUejNleNkx1","weight":"315"}],[],[],[],[],[],[{"comments":"abc","movement":"Strict Press","userID":"wDHZv3OL55SIymHkhMUejNleNkx1","weight":"155"}]];
var result = _.filter(arrs, _.size);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>

If all the items in the top level array are arrays then you could lodash's reject with the isEmpty predicate.
let result = _.reject(data, _.isEmpty);
isEmpty will also return true for empty objects amongst other thing so if your top level array can contain such items then to just remove empty arrays you could compose a new function to return just empty arrays and use that as the predicate to reject:
let isEmptyArray = item => _.isArray(item) && _.isEmpty(item);
let result = _.reject(data, isEmptyArray);

Test each array in turn to see if it has a non-zero (truthy) length. If it does, put it in your new array.
var array_of_arrays = [[1], [1,1], [], [], [1]];
var array_of_non_empty_arrays = array_of_arrays.filter((array) => array.length);
console.log(array_of_non_empty_arrays);

Related

Split array elements dynamically

I have below scenarios to handle.
let data = [
[ "ALISHA", "SUICA", "PASMO" ],
[ "HARMONY" ],
[ "OCTOPUS" ]
]
let data1 = [
[ "ALISHA", ],
[ "HARMONY" ],
[ "OCTOPUS", "SUICA", "PASMO" ]
]
For both of the above data, i want the result to look like this.
let result = [
[ "ALISHA" ],
[ "HARMONY" ],
[ "OCTOPUS" ],
[ "SUICA" ],
[ "PASMO" ]
]
Can someone please let me know how to achieve this. I tried the following but no success
let result = []
for (let i = 0; i < data.length; i++) {
let split = data[i].split(","); // just split once
result.push(split[0]); // before the comma
}
we will use forEach method on main array inside forEach we will use if condition if is array and length more than 1 will add another forEach method and push sub array to main array after that remove sub array
look like that
let data = [
["ALISHA"],
["HARMONY"],
["OCTOPUS", "SUICA", "PASMO"]
]
data.forEach((cur, index) => {
if (Array.isArray(cur) && cur.length > 1) {
cur.forEach(cur => data.push([cur]))
data.splice(index, 1);
}
})
console.log(data)
Uses Array.reduce extract all elements, then convert to [string] by Array.map.
const data = [
[ "ALISHA" ],
[ "HARMONY" ],
[ "OCTOPUS", "SUICA", "PASMO" ]
]
console.log(
data.reduce((pre, cur) => [...pre, ...cur], []).map(item => [item])
// data.reduce((pre, cur) => pre.concat(...cur), []).map(item => [item]) // different methods but same logic (uses Array.concat instead of spread operator)
)
You can use flat and map
const data = [["ALISHA"], ["HARMONY"], ["OCTOPUS", "SUICA", "PASMO"]];
const result = data.flat().map((a) => [a]);
console.log(result);

Iterating through a matrix of irregular row lengths

I've got a matrix where rows don't necessarily have the same length:
The following are musical tokens in the format of solfege.
const notes = [
[ 'do5', 'mi5' ],
[ 'mi6', 'so6', 'ti6', 're7' ],
[ 'so7', 'ti7', 're8', 'fa8' ],
[ 'la3', 'do4', 'mi4' ],
[ 'fa2', 'la2' ],
[ 're2' ],
[ 'ti1', 're2', 'fa2' ]
];
I have a function that converts these tokens into equivalent alphabetic tokens (for example: fa2 would be converted to F2 using my function).
I would like to be able to iterate over this matrix, and return the transformed matrix, which should retain the same dimensions.
Thanks,
Nakul
Here's what you probably want:
const notes = [
[ 'do5', 'mi5' ],
[ 'mi6', 'so6', 'ti6', 're7' ],
[ 'so7', 'ti7', 're8', 'fa8' ],
[ 'la3', 'do4', 'mi4' ],
[ 'fa2', 'la2' ],
[ 're2' ],
[ 'ti1', 're2', 'fa2' ]
];
// replace this function with your own converter
function convert(note) {
return note.toUpperCase();
}
for (let i = 0; i < notes.length; i++) { // for each row
// map will iterate through the row, converting each note
notes[i] = notes[i].map(convert);
}
The part map(convert) is just a shorter form of map(note => convert(note)).
This is not very efficient, as map() will create a new array for each row, but in your case it's probably more important that the code is readable rather than performant, so that's fine.
You can use the new Array.prototype.flat() function, but, if you want wider support (.flat() is ignored by both Edge and IE), then I would use two for..of loops.
const arr = [
[ 'do5', 'mi5' ],
[ 'mi6', 'so6', 'ti6', 're7' ],
[ 'so7', 'ti7', 're8', 'fa8' ],
[ 'la3', 'do4', 'mi4' ],
[ 'fa2', 'la2' ],
[ 're2' ],
[ 'ti1', 're2', 'fa2' ]
];
// Modern JavaScript
for (const item of arr.flat()) {
console.log(item);
}
console.log('----');
// More widely supported JavaScript
for (const subarray of arr) {
for (const subitem of subarray) {
console.log(subitem);
}
}

adding copy of array to another array

I am trying to add a copy of an array to another array with array.slice(), but when I update the original, it updates the copy that I added. How can I add a copy that isn't altered when the original is altered?
I've tried using result.unshift[...curRow], and result.unshift(curRow.slice()) when I add
function game(n) {
var result = [];
let row1=[[1, 2]];
for (var i=1;i<n;i++){
var cur = row1[i];
var prev = row1[i - 1];
row1.push([prev[0] + 1, prev[1] + 1]);
}
result.push(row1.slice());
let curRow = row1.slice();
for (var i=1;i<n;i++){
for (var j = 0; j<curRow.length ;j++){
curRow[j][1]++;
}
result.unshift(curRow.slice());
console.log('curRow =',curRow);
console.log('result = ', result)
}
console.log('result = ', result)
return result
}
game(3);
This is my current output:
'result ='[ [ [ 1, 2 ], [ 2, 3 ], [ 3, 4 ] ] ]
'result = '[ [ [ 1, 3 ], [ 2, 4 ], [ 3, 5 ] ], [ [ 1, 3 ], [ 2, 4 ], [ 3, 5 ] ] ]
'result = '[ [ [ 1, 4 ], [ 2, 5 ], [ 3, 6 ] ], [ [ 1, 4 ], [ 2, 5 ], [ 3, 6 ] ], [ [ 1, 4 ], [ 2, 5 ], [ 3, 6 ] ] ]
I want the result array to contain each iteration of the curRow array, instead of just having copies of the latest one.
In JavaScript, objects/arrays, also known as non-primitive types, are given a reference, rather than value. Therefore, this particular reference points to the object's location in the memory. That variable you are working with has a 'reference' rather than containing an actual 'value'. This is why it is mutated when you use it on the game() function.
To work with that, you should create a shallow copy of that array, before making any manipulation/mutation to it.
const copy = originalArray.map(element => ({...element}));
Now, all changes to the copy array will not be applied to originalArray.
Because in Javascript Arrays are handled by reference, when you reasign an array to a new variable, the new variable only holds a reference to the original array. Any alteration on the original array will be reflected in any of its instances/references (beucause they are essentially the same object).
Since the ES6 version of the standard we have Array.from() to perform copies or duplicates of the contents of an Array.
It's my favorite way to go, is fast, safe and readable.
The Array.from() method creates a new, shallow-copied Array instance from an array-like or iterable object. Extended documentation here
Check the example :
let a = [1,2,3,4,5];
let b = Array.from(a);
// modify first array to test
a[0] = 666;
// output second array to check
console.log(b)
// [1,2,3,4,5] OK!
you can use this concept:
firstArray=firstArray.concat(...secondArray);

how to find a filtered array in lodash?

i am working on lodash in which i need to filtered the data from big collection of array.The collection of array is this type of data:
[ { poll_options: [ [Object] ],
responder_ids: [ '5a7189c14615db31ecd54347', '59ffb41f62d346204e0a199b' ],
voted_ids: [ '5a7189c14615db31ecd54347' ],
_id: 5a7878833a1bf4238ddc5cef },
{ poll_options: [ [Object] ],
responder_ids: [ '5a7189c14615db31ecd54347' ],
voted_ids: [ '5a7189c14615db31ecd54347' ],
_id: 5a787756217db51698ea8fd6 } ]
I want to filter array of which contain the value of ids which is not in voted_ids(i.e for first object it should return this 59ffb41f62d346204e0a199b nd for second collections it should return empty array) that means i have to return only those values which are not on voted_ids but in responder_ids. my code is this
_.map(polls,(poll) => {
// console.log(poll.responder_ids)
return _.filter(poll.responder_ids,(responder) => {
return _.find(poll.voted_ids,(voted) => {
return !_.includes(responder,voted)
})
but it does not returning filtered array istead it return whole collections.Where I am doing wrong??
Now its returning result like this...
[ [ '59ffb41f62d346204e0a199b' ], [] ]
I want single array not multiarray.
You are using _.filter in a wrong way.
Filter iterates over all items in your array. If the filter function returns true the item will be returned in the filtered array and if it returns false it will not be returned.
You however are returning an array, which results to truthy and therefore all items are returned.
What you want is something like:
const polls = [
{
poll_options: [ '[Object]' ],
responder_ids: [ '5a7189c14615db31ecd54347', '59ffb41f62d346204e0a199b' ],
voted_ids: [ '5a7189c14615db31ecd54347' ],
_id: '5a7878833a1bf4238ddc5cef'
},
{
poll_options: [ '[Object]' ],
responder_ids: [ '5a7189c14615db31ecd54347' ],
voted_ids: [ '5a7189c14615db31ecd54347' ],
_id: '5a787756217db51698ea8fd6'
}
];
let ids = [];
for (let i = 0; i < polls.length; i++) {
ids = [...ids, ...polls[i].responder_ids.filter(id => {
return !polls[i].voted_ids.includes(id);
})];
}
console.log(ids);
This is not right way to use filter function. In the callback function you should return some boolean value so the array could be filtered by that criteria.
If I understand you correctly you want to filter through array to get array which will contain all results when the id is in the responder_ids but no in the voted_ids. It could be resolved in various ways, i.e.:
_.filter(polls, poll => _.findIndex(poll.responder_ids, id => id === '59ffb41f62d346204e0a199b') > -1 && _.findIndex(poll.voted_ids, id => id === '59ffb41f62d346204e0a199b') === -1;
);

find unique entries inside a multidimensional javascript array, order important dependent on level

What would be the most elegant solution to find all unique first level entries inside a multidimensional javascript array? There is only one important rule: the order of the entries is important only on the first level, but not important on the second level
For example, for the following array the script should return 4 unique entries (the first, the third, the fourth and the fifth):
[
[ [],[22],[1,13,17],[12],[] ],
[ [],[22],[17,13,1],[12],[] ],
[ [],[12],[1,13,17],[22],[] ],
[ [11],[12],[13],[14],[15] ],
[ [15],[14],[13],[12],[11] ]
]
PS. jQuery can be used as well.
First of all, here is a working JSFiddle for you to play around with: http://jsfiddle.net/missyalyssi/ro8o94nk/
Given an input array the function findUnique will return an array containing items that are unique according to your definition. So, for example:
[[8],[1,2,3],[9]] is a duplicate of [[8], [3,1,2], [9]] but it is not a duplicate of [[9], [3,1,2], [8]]
My main focus when writing this was to make it easy to read and understand.
function findUnique(input) {
let found = [];
let uniqueEls = new Set();
let hasDup = true;
for (let element of input) {
hasDup = found.length &&
found.every((el) => {return deepEqualsNaive(el, element)});
if (hasDup) {
uniqueEls.delete(element);
continue;
}
found.push(element);
uniqueEls.add(element);
}
return [...uniqueEls];
}
This function uses deepEqualsNaive to determine if two arrays are equal. Since object equality in javascript means that the arrays would point to the same memory location we need to build our own function to return true for what we are calling equal. Here, by equal we mean that they have the same elements even though they are not pointing to the same memory location, or appearing in the same order.
I have written this function recursively for readability I do not know the context that you are using this in. If you could overflow the stack then use an iterative version.
Here are some example inputs and what we would expect:
deepEqualsNaive([ [],[22],[1,13,17],[12],[] ], [ [],[22],[17,13,1],[12],[] ]) => true
deepEqualsNaive([ [],[22],[17,13,1],[12],[] ], [ [],[12],[1,13,17],[22],[] ]) => false
deepEqualsNaive([ [],[22],[1,13,17],[12],[] ], [ [],22,[17,13,1],[12],[] ]) => false
The function:
function deepEqualsNaive (input, clone) {
if (!Array.isArray(input) || !Array.isArray(clone)) return false;
if (input.length !== clone.length) return false;
var result = 0;
for (let elIdx = 0; elIdx < input.length; elIdx++) {
var tryDeep = true;
if (Array.isArray(input[elIdx])) tryDeep = deepEqualsNaive(input[elIdx], clone[elIdx]);
if (!tryDeep) return false;
result ^= input[elIdx];
result ^= clone[elIdx];
}
return result === 0;
}
If you're not all that worried about performance and just need something that works, you could use the constant depth you mentioned along with the string representation as a "fingerprint" of sorts (akin to Java's hashcode).
Then you use a Set to keep track of items you've not seen before, and add only those that are new.
function getUnique(rows) {
let unique = new Set();
let results = [];
for (let row of rows) {
// Fingerprint is the string representation of the row,
// with the inner-level sorted (as order doesn't matter).
// E.g., fingerprint of [ [8], [3, 2, 1], [9] ] is '[[8],[1,2,3],[9]]'
let fingerprint = JSON.stringify(row.map((cells) => {
return cells.concat().sort(); // Use concat to avoid sorting in place.
}));
// If we haven't seen this fingerprint before,
// add to the filter and the results list.
if (!unique.has(fingerprint)) {
unique.add(fingerprint);
results.push(row);
}
}
return results;
}
This, for example, will come up with...
> x = [
... [ [8], [3, 2, 1], [9] ],
... [ [7], [8, 3, 9], [1, 2] ],
... [ [8], [1, 2, 3], [9] ],
... ];
> getUnique(x);
[ [ [ 8 ], [ 3, 2, 1 ], [ 9 ] ],
[ [ 7 ], [ 8, 3, 9 ], [ 1, 2 ] ] ]
Obviously if your inner values are non-primitives (objects, arrays, etc) then this will fall over, but if you're dealing with numbers like your example, it should be fine.
If it's ok to have reference to the original array 'records' and inner arrays (that is, no deep copy), you can use something like:
function distinct(arr){
const res =[], //array with results
cmpArr = (a1,a2) => a1.length===a2.length && a1.every((i,ind) => a2[ind] === i),
cmpRec = (a1,a2) => [1,2,3].every(i=> cmpArr(a1[i],a2[i])); //compare 'records' for indices 1,2 and 3
for(let subarr of arr){
subarr[2].sort(); //NB, this alters the source array. If this is not allowed, a work around can be created
if(!res.some(r => cmpRec(r,subarr))) //check if 'res' doesn't have an entry , based on the cmpRec function
res.push(subarr);
}
return res;
}
//test:
let input = [
[ [],[22],[1,13,17],[12],[] ],
[ [],[22],[17,13,1],[12],[] ],
[ [],[12],[1,13,17],[22],[] ],
[ [11],[12],[13],[14],[15] ],
[ [15],[14],[13],[12],[11] ]
];
console.log(distinct(input).map(JSON.stringify));

Categories

Resources