Javascript - Index Each Element in an Array/List - javascript

> var lst = ['red', 'blue', 'yellow'];
> someFunc(lst);
[('red', 0), ('blue', 1), ('yellow', 2)]
Is there any way to do this in Javascript/JQuery? I know that I can simply just make a for loop and convert each of the original list's element to what I want, but I was wondering if there was a built in way of doing this.
Thanks!

You could use Array#map and return for each item an array with the value and the index.
var lst = ['red', 'blue', 'yellow'],
array = lst.map(function (a, i) { return [a, i]; });
console.log(array);

You can leverage the map() function that is providing you both the value and and index of each item in the array:
lst.map(function(v, i) { return { value: v, index: i }; } );
See MDN

With map method in ES6 :
var lst = ['red', 'blue', 'yellow'];
var array = lst.map((item, index) => [item, index])
console.log(array); // [ [ 'red', 0 ], [ 'blue', 1 ], [ 'yellow', 2 ] ]

Related

Best way to re arrange array of object?

I have an array or object which will serve as columns for a table with a unique key
const list = [{
key: "Name",
textColor: "red"
},{
key: "Age",
textColor: "green"
},{
key: "Occupation",
textColor: "yellow"
}]
And, I have a list of ordering of columns in the table
const newOrder = ["Occupation", "Name", "Age"]
Now , how can i rearrange the list according to the newOrder without using nested loops. Also, these all are dyanamic, so its not just about the above mentioned three columns
Expected Output
const list = [{
key: "Occupation",
textColor: "yellow"
},{
key: "Name",
textColor: "red"
},{
key: "Age",
textColor: "green"
}]
Your list can be reformatted to a regular javascript object in which key is the property name, and textColor is the value:
const toObject = kvps => Object.fromEntries(kvps.map(kvp => [ kvp.key, kvp.textColor ]));
With a given array of keys, you can pick values from that object like so:
const fromObject = (order, obj) => order.map(key => ({ key, textColor: obj[key] }));
Chain the two together, and you can reorder any list of key value pairs:
const list = [{
key: "Name",
textColor: "red"
},{
key: "Age",
textColor: "green"
},{
key: "Occupation",
textColor: "yellow"
}]
const toObject = kvps => Object.fromEntries(kvps.map(kvp => [ kvp.key, kvp.textColor ]));
const fromObject = (order, obj) => order.map(key => ({ key, textColor: obj[key] }));
const reorder = (order, kvps) => fromObject(order, toObject(kvps));
const newList = reorder(["Occupation", "Name", "Age"], list);
console.log(
newList
)
Edit: if the sizes of your list and order arrays are small, you probably want to go with the much easier to read approach suggested by Jon Webb in one of the other answers. 🙂 I tried to keep my solution to an O(n + m) complexity rather than O(n * m), (n = list size, m = order size) but it's probably not worth the added complexity.
You can iterate on the "order" list, finding the corresponding item in the original list, and push the item to the new list in that order:
const orderList = (list, order) => {
const newList = [];
for (key of order) {
const item = list.find((obj) => obj.key == key);
if (item) newList.push(item);
}
return newList;
}
You can use the sort method on the array. The sort method will sort in place so you should copy your array if you dont want to mutate the original.
The array sort method takes a compare function the receives two elements for comparison a and b. It should return a number and it will sort them depending on that number:
If > 0 then b is before a
If < 0 then b is after a
If 0 then keep as is
By using indexOf on the newOrder array can get the index of the key. And index of 0 should come before and index of 1 should come before and index of 2 of course. So if the index of a.key is 2 and the index of b.key is 0, then we should return a value greater than 0 since b should come before a.
In my implementation below I'm cloning the original list ([...list]) as to not mutate accidentally. You could just as well do list.sort(...) if you don't need or care about mutating.
const list = [{
key: "Name",
textColor: "red"
},{
key: "Age",
textColor: "green"
},{
key: "Occupation",
textColor: "yellow"
}]
const newOrder = ["Occupation", "Name", "Age"]
function sort(list, order) {
return [...list].sort((a, b) => {
const keyIndexA = order.indexOf(a.key);
const keyIndexB = order.indexOf(b.key);
if (keyIndexA < keyIndexB) return -1;
if (keyIndexA > keyIndexB) return 1;
return 0;
});
}
console.log(sort(list, newOrder));
You can use just regular sort
const list = [{key: "Name",textColor: "red"},{key: "Age",textColor: "green"},{key: "Occupation",textColor: "yellow"}];
const newOrder = ["Occupation", "Name", "Age"];
const result = list.sort(({key: a}, {key: b}) => newOrder.indexOf(a) - newOrder.indexOf(b));
console.log(result);
.as-console-wrapper{min-height: 100%!important; top: 0}
You can try to loop through the newOrder array, find the object that correlates to the first item and push to a new array.
const orderedList = [];
newOrder.forEach(order => {
orderedList.push(list.find(({key}) => key === order));
})
You can use orderBy from lodash library

remove specific element from all arrays inside object

I want to remove all 'main-1-3' elements inside arrays:
const a = {
white: ['main-1-1'],
red: ['main-1-3'],
orange: [],
green: [],
blue: [],
}
const colors = ['white', 'red', 'orange', 'green', 'blue'];
for(let i = 0; i < colors.length; i++) {
a[colors[i]].splice(a[colors[i]].indexOf('main-1-3'), 1);
}
console.log(a)
But as you see if there is no such element other elements are removed too! I want to only remove the 'main-1-3' in all of the arrays?
How can I fix this?
You should first check that the item really is in the array. indexOf an item that isn't in the array will return -1, which will mess up the splice call:
const a = {
white: ['main-1-1'],
red: ['main-1-3'],
orange: [],
green: [],
blue: [],
}
const colors = ['white', 'red', 'orange', 'green', 'blue'];
for(let i = 0; i < colors.length; i++) {
const index = a[colors[i]].indexOf('main-1-3');
if (index >= 0) {
a[colors[i]].splice(index, 1);
}
}
console.log(a)
You could just loop over a's Object.entries and filter out all the main-1-3 elements.
const a = {
white: ['main-1-1'],
red: ['main-1-3', 'main-1-1'],
orange: [],
green: [],
blue: ['main-1-2'],
}
for (let [key, arr] of Object.entries(a)) {
a[key] = arr.filter(el => el !== 'main-1-3');
}
console.log(a);
iterate through each array and filter out the item main-1-3:
const a = {
white: ['main-1-1'],
red: ['main-1-3'],
orange: [],
green: [],
blue: [],
}
const colors = ['white', 'red', 'orange', 'green', 'blue'];
colors.forEach(e => a[e] = a[e].filter(n => n !== 'main-1-3'))
console.log(a)
Just check whether string 'main-1-3' exist in arrays in a.
Because if the string main-1-3 doesnot exist in the arrays in object a, the index will return -1.
Array.splice(-1, 1) will delete the last node in the array, this is what happening in your case.
Array.splice(-1, 1) Example
const myArray = [1, 2, 3, 4];
myArray.splice(-1, 1);
console.log(myArray); // Last element removed
As you see in the above example, Array.splice(-1, 1) deletes the last node. So you have to ensure that your index a[colors[i]].indexOf('main-1-3') is not -1 or else the last nodes in your array will be deleted.
Working Fiddle
const a = {
white: ['main-1-1'],
red: ['main-1-3'],
orange: [],
green: [],
blue: [],
}
const colors = ['white', 'red', 'orange', 'green', 'blue'];
for (let i = 0; i < colors.length; i++) {
const index = a[colors[i]].indexOf('main-1-3');
if (index > -1) {
a[colors[i]].splice(index, 1);
}
}
console.log(a)

Reduce an array to groups

I have this array:
const vals = ['blue', 'blue', 'green', 'blue', 'yellow', 'yellow', 'green']
I would like to reduce it to this:
['blueblue', 'green', 'blue', 'yellowyellow', 'green']
Where it concats the values if they are the same. Once the value changes it starts again.
Trying to use reduce but not sure how to make it work as the acc needs to be a string and an array depending on if the value is the same or not!
let lastType = vals[0]
const groups = vals.reduce((acc, value) => {
if (lastType === value) {
acc += value // string
}
lastType = value
return acc.push(value)
}, [])
The final result is an array, so that is what acc should be.
Instead of appending value to acc, append it to the last element of the array:
const vals = ['blue', 'blue', 'green', 'blue', 'yellow', 'yellow', 'green']
let lastType = null;
const groups = vals.reduce((acc, value) => {
if (lastType === value) {
acc[acc.length - 1] += value;
} else {
lastType = value
acc.push(value)
}
return acc;
}, [])
console.log(groups);
The use of lastType in a closure is a bit of a code smell, as is the use of mutability in the reducer.
A preferable, though slightly more verbose approach:
const vals = ['blue', 'blue', 'green', 'blue', 'yellow', 'yellow', 'green']
const { groups } = vals.reduce(({ lastType, groups }, value) => {
if (lastType === value) {
return {
lastType,
groups: [
...groups.slice(0, groups.length - 2),
groups[groups.length - 1] + value
],
};
}
return {
lastType: value,
groups: [...groups, value],
};
}, { groups: [], lastType: null })
console.log(groups);
You need to check the element at the index in front of the actual index and add a new string to the accumulator.
const
values = ['blue', 'blue', 'green', 'blue', 'yellow', 'yellow', 'green'],
result = values.reduce((accumulator, value, index, array) => {
if (value === array[index - 1]) accumulator[accumulator.length - 1] += value;
else accumulator.push(value);
return accumulator;
}, []);
console.log(result);
Push an empty string to the accumulator if the type is new. Remember that .push returns the new length of the array, so don't return it at the bottom of the function - instead, return the whole accumulator:
const vals = ['blue', 'blue', 'green', 'blue', 'yellow', 'yellow', 'green']
let lastType;
const groups = vals.reduce((acc, value) => {
if (lastType !== value) {
acc.push('');
lastType = value;
}
acc[acc.length - 1] += value;
return acc;
}, [])
console.log(groups);
You need to keep track of a bit more. Basic way of doing it is using an bject to hold the state.
const vals = ['blue', 'blue', 'green', 'blue', 'yellow', 'yellow', 'green']
const groups = vals.reduce((acc, value, index, array) => {
// is it the same, duplicate it
if (acc.lastType === value) {
acc.current += value;
} else {
// did we have a previous value? Add it to the array
if (acc.lastType) {
acc.result.push(acc.current);
}
// set the current type
acc.current = value;
acc.lastType = value;
}
// if we are at the end, add what we have to the array
if (index+1===array.length) {
acc.result.push(acc.current);
}
return acc;
}, { result: [], current: '', lastType: null }).result;
console.log(groups);

JavaScript - array, object - How can I streamline this code

How can I streamline this code ?
I think it might have better way to refactor .
const aa = ['red', 'yellow', 'blue']
const bb = { first: 0, second: 1, third: 2 }
const cc = { ...bb }
cc.first = aa[cc.first]
cc.second = aa[cc.second]
cc.third = aa[cc.third]
You can use .reduce() and Object.entries() methods:
const aa = ['red', 'yellow', 'blue']
const bb = { first: 0, second: 1, third: 2 }
const cc = Object.entries(bb)
.reduce((r, [k, i]) => (r[k] = aa[i], r), {});
console.log(cc);
This is a solution that still works after adding or removing an item from aa or bb, without need to change your code.
const aa = ['red', 'yellow', 'blue']
const bb = { first: 0, second: 1, third: 2 }
const cc = {};
for(var key in bb)
cc[key] = aa[bb[key]];

Find last matching object in array of objects

I have an array of objects. I need to get the object type ("shape" in this example) of the last object, remove it, and then find the index of the previous object in the array that has the same type, e.g. "shape".
var fruits = [
{
shape: round,
name: orange
},
{
shape: round,
name: apple
},
{
shape: oblong,
name: zucchini
},
{
shape: oblong,
name: banana
},
{
shape: round,
name: grapefruit
}
]
// What's the shape of the last fruit
var currentShape = fruits[fruits.length-1].shape;
// Remove last fruit
fruits.pop(); // grapefruit removed
// Find the index of the last round fruit
var previousInShapeType = fruits.lastIndexOf(currentShape);
// should find apple, index = 1
So, obviously the type in this example will be "round". But I'm not looking for an array value of "round". I'm looking for where fruits.shape = round.
var previousInShapeType = fruits.lastIndexOf(fruits.shape = currentShape);
But just using that doesn't work. I'm sure I'm missing something simple. How do I find the last item in the array where the shape of the object = round?
var fruit = fruits.slice().reverse().find(fruit => fruit.shape === currentShape);
You can transform your array to an array boolean type and get the last true index.
const lastIndex = fruits.map(fruit =>
fruit.shape === currentShape).lastIndexOf(true);
var previousInShapeType, index = fruits.length - 1;
for ( ; index >= 0; index--) {
if (fruits[index].shape == currentShape) {
previousInShapeType = fruits[index];
break;
}
}
You can also loop backwards through array.
Fiddle: http://jsfiddle.net/vonn9xhm/
Using the Lodash library, you can find the last logical element.
_.findLast([1,2,3,5,4], n => n % 2 == 1); // Find last odd element
// expected output: 5
An easier and relatively efficient solution. Filter and pop!
Filter all fruits matching the current shape and then pop to get the last one.
fruits.filter(({shape}) => shape === currentShape).pop()
var fruits = [{
shape: 'round',
name: 'orange'
}, {
shape: 'round',
name: 'apple'
}, {
shape: 'oblong',
name: 'zucchini'
}, {
shape: 'oblong',
name: 'banana'
}, {
shape: 'round',
name: 'grapefruit'
}];
// What's the shape of the last fruit
var currentShape = fruits[fruits.length - 1].shape;
// Remove last fruit
fruits.pop(); // grapefruit removed
alert(fruits.filter(({shape}) => shape === currentShape).pop().name);
Update - 27 October 2021 (Chrome 97+)
Proposal for Array.prototype.findLast and Array.prototype.findLastIndex is now on Stage 3 4!
Here's how you can use those:
const fruits = [
{ shape: 'round', name: 'orange' },
{ shape: 'round', name: 'apple' },
{ shape: 'oblong', name: 'zucchini' },
{ shape: 'oblong', name: 'banana' },
{ shape: 'round', name: 'grapefruit' }
]
let last_element = fruits.findLast((item) => item.shape === 'oblong');
// → { shape: oblong, name: banana }
let last_element_index = fruits.findLastIndex((item) => item.shape === 'oblong');
// → 3
You can read more in this V8 blog post.
You can find more in "New in Chrome" series.
This is a solution that does not depend on reverse, and therefore does not require "cloning" the original collection.
const lastShapeIndex = fruits.reduce((acc, fruit, index) => (
fruit.shape === currentShape ? index : acc
), -1);
Based on Luke Liu's answer, but using ES6's spread operator to make it a bit easier to read:
const fruit = [...fruits].reverse().find(fruit => fruit.shape === currentShape);
Update - Array.prototype.findLast() is now available for use
var fruits = [
{
shape: 'round',
name: 'orange'
},
{
shape: 'round',
name: 'apple'
},
{
shape: 'oblong',
name: 'zucchini'
},
{
shape: 'oblong',
name: 'banana'
},
{
shape: 'round',
name: 'grapefruit'
}
]
const last = fruits.findLast(n => n.shape === 'oblong');
console.log(last);
**Please check out browser compatibly before using it in this link
Read more about findLast here
Another way to achieve this is using the reverse (but less efficient)
var fruits = [
{
shape: 'round',
name: 'orange'
},
{
shape: 'round',
name: 'apple'
},
{
shape: 'oblong',
name: 'zucchini'
},
{
shape: 'oblong',
name: 'banana'
},
{
shape: 'round',
name: 'grapefruit'
}
]
const last = fruits.reverse().find(n => n.shape === 'oblong');
console.log(last);
plain JS:
var len = fruits.length, prev = false;
while(!prev && len--){
(fruits[len].shape == currentShape) && (prev = fruits[len]);
}
lodash:
_.findLast(fruits, 'shape', currentShape);
While the currently accepted answer will do the trick, the arrival of ES6 (ECMA2015) added the spread operator which makes it easy to duplicate your array (this will work fine for the fruit array in your example but beware of nested arrays). You could also make use of the fact that the pop method returns the removed element to make your code more concise. Hence you could achieve the desired result with the following 2 lines of code
const currentShape = fruits.pop().shape;
const previousInShapeType = [...fruits].reverse().find(
fruit => fruit.shape === currentShape
);
I would suggest another nice solution which doesn't bother cloning a new object using reverse().
I use reduceRight to does the job instead.
function findLastIndex(array, fn) {
if (!array) return -1;
if (!fn || typeof fn !== "function") throw `${fn} is not a function`;
return array.reduceRight((prev, currentValue, currentIndex) => {
if (prev > -1) return prev;
if (fn(currentValue, currentIndex)) return currentIndex;
return -1;
}, -1);
}
And usage
findLastIndex([1,2,3,4,5,6,7,5,4,2,1], (current, index) => current === 2); // return 9
findLastIndex([{id: 1},{id: 2},{id: 1}], (current, index) => current.id === 1); //return 2
Here's a typescript version:
/**
* Returns the value of the last element in the array where predicate is true, and undefined
* otherwise. It's similar to the native find method, but searches in descending order.
* #param list the array to search in.
* #param predicate find calls predicate once for each element of the array, in descending
* order, until it finds one where predicate returns true. If such an element is found, find
* immediately returns that element value. Otherwise, find returns undefined.
*/
export function findLast<T>(
list: Array<T>,
predicate: (value: T, index: number, obj: T[]) => unknown
): T | undefined {
for (let index = list.length - 1; index >= 0; index--) {
let currentValue = list[index];
let predicateResult = predicate(currentValue, index, list);
if (predicateResult) {
return currentValue;
}
}
return undefined;
}
Usage:
const r = findLast([12, 43, 5436, 44, 4], v => v < 45);
console.log(r); // 4
You should use filter! filter takes a function as an argument, and returns a new array.
var roundFruits = fruits.filter(function(d) {
// d is each element of the original array
return d.shape == "round";
});
Now roundFruits will contain the elements of the original array for which the function returns true. Now if you want to know the original array indexes, never fear - you can use the function map. map also operates on an array, and takes a function which acts on the array. we can chain map and filter together as follows
var roundFruits = fruits.map(function(d, i) {
// d is each element, i is the index
d.i = i; // create index variable
return d;
}).filter(function(d) {
return d.shape == "round"
});
The resulting array will contain all objects in the original fruits array for which the shape is round, and their original index in the fruits array.
roundFruits = [
{
shape: round,
name: orange,
i: 0
},
{
shape: round,
name: apple,
i: 1
},
{
shape: round,
name: grapefruit
i: 4
}
]
Now you can do whatever you need to with the exact knowledge of the location of the relevant data.
// get last round element
fruits[4];
findLastIndex and findLast are now natively supported across all major browsers (except IE).
Referring to your example, you can find the index of the last item that matches your condition as follows:
var previousInShapeType = fruits.findLastIndex((fruit) => fruit.shape === currentShape);
findLast works exactly the same but returns an object instead.
Reference to MDN documentation for findLastIndex and findLast.

Categories