0

UPDATE: Solved!

The only change needed was making the await of the API call to a return, then the map was returned with both image names and ids. Still trying to get the map converted to an array, but this question for this post is solved. Here's the new printout:

1 face detected from image Family1-Son1.jpg with ID 98091e1e-bc8d-4c93-a850-a115684a6e6e
Family1-Son1.jpg
1 face detected from image Family1-Dad3.jpg with ID f94360f5-feb3-4d14-816f-7d854fc0b34c
Family1-Dad3.jpg
[ { '0': 'F',
    '1': 'a',
    '2': 'm',
    '3': 'i',
    '4': 'l',
    '5': 'y',
    '6': '1',
    '7': '-',
    '8': 'D',
    '9': 'a',
    '10': 'd',
    '11': '3',
    '12': '.',
    '13': 'j',
    '14': 'p',
    '15': 'g',
    id: 'f94360f5-feb3-4d14-816f-7d854fc0b34c' },
  { '0': 'F',
    '1': 'a',
    '2': 'm',
    '3': 'i',
    '4': 'l',
    '5': 'y',
    '6': '1',
    '7': '-',
    '8': 'S',
    '9': 'o',
    '10': 'n',
    '11': '1',
    '12': '.',
    '13': 'j',
    '14': 'p',
    '15': 'g',
    id: '98091e1e-bc8d-4c93-a850-a115684a6e6e' } ]
[ <2 empty items> ]
[]

I've tried many different methods on here, can't get them to work. This one is the closest I came. I am trying to call an API but on each item in an array. I can't do this in a regular loop, so many reasons why not. So someone said use the array.map() function instead of a loop. I got this far:

const IMAGE_BASE_URL = 'https://csdx.blob.core.windows.net/resources/Face/Images/'
let sourceImageFileNames = ['Family1-Dad3.jpg', 'Family1-Son1.jpg']

// Detect faces in the source image array, then get their IDs
let sourcefaceMap = await Promise.all(sourceImageFileNames.map(async (imageName) => {
   // Returns a Promise<DetectedFace[]>
    await client.face.detectWithUrl(IMAGE_BASE_URL + imageName)
        .then((faces) => {
            console.log(`${faces.length} face detected from image ${imageName} with ID ${faces[0].faceId}`)
            let id = faces[0].faceId
            return { ...imageName, id } 
        }).catch((err) => {
            console.log(`No face detected in: ${sourceImageFileNames[0]}.`)
            throw err;
        })
}))

let values = Object.values(sourcefaceMap)
console.log(values)

// Create an array to store the source face IDs
var sourceFaceIds = new Array(sourceImageFileNames.length)
console.log(sourceFaceIds)
let ids = sourceFaceIds.filter((id)=> {
    return id != null;
})

console.log(ids)

The values seem to be there in when debugging, but then when I try to return the map, it prints out as undefined. Even though the map does do the job of looping to get each id (as seen in the top two print statements). Here is my printout:

VERIFY
1 face(s) detected from image Family1-Dad3.jpg with ID f94360f5-feb3-4d14-816f-7d854fc0b34c
1 face(s) detected from image Family1-Son1.jpg with ID 98091e1e-bc8d-4c93-a850-a115684a6e6e
[ undefined, undefined ]
[ <2 empty items> ]
[ <2 empty items> ]

Here is a screenshot of the id having value, when I hover over it in the return statement: enter image description here

Basically, I am trying to do an API call with URL images, then the API will associate an ID with each face in the image. I need the API call to return all those IDs. In this case, it's only 2 IDs. How do I do this? At the end of my code, I just need an array of those IDs. That's all. Thanks.

Azurespot
  • 3,066
  • 3
  • 45
  • 73
  • can you edit your question, let me know what output you need at the end – Narendra Chouhan Oct 09 '19 at 04:59
  • Thanks @NarendraChouhan, it says in the last paragraph I need an array of the IDs. So if I print `sourceFaceIds` it would print an array. I need an array of IDs for another API call that comes later (not shown, since not relevant to this problem). It's weird because this code worked for someone else on here, Not sure why the `id` variable is not getting used in the return statement. – Azurespot Oct 09 '19 at 05:16
  • @NarendraChouhan I edited the code a little, I fixed some unrelated errors, and added some comments to try to make it more clear what the architecture as a whole is doing. Take a fresh look if you have time, thanks. – Azurespot Oct 09 '19 at 18:58
  • What is `new Array(sourceImageFileNames.length)` supposed to accomplish? I doubt [this does](https://stackoverflow.com/questions/11266126/undefined-values-in-arraylen-initializer) what you expect. – Bergi Oct 09 '19 at 19:11
  • @Bergi wow, thanks for pointing that out. I'm used to other languages, Node has so many callbacks, they trip me up. I just wanted to convert the map's values (actually only the very last value of the key/value pair -- the IDs) and store them into an array. So in the end I have an array of two IDs, in this case. Is there a better way to instantiate an array in this case? I guess I could just get the last map values and define a new array with them (instead of creating an empty one to start). I will try this! Thanks! – Azurespot Oct 09 '19 at 19:26
  • @Azurespot Just `return id` in the `then` callback in the `map` then, and the array of ids will come out of `Promise.all`. If you really want to post-process the values, use `ids = values.filter(…)` or `ids = values.map(…)`, not that empty `sourceFaceIds` array – Bergi Oct 09 '19 at 19:48

2 Answers2

1

Your map callback didn't return anything, it only waited. Use

const values = await Promise.all(sourceImageFileNames.map(imageName => {
    // now *really* returns a Promise<DetectedFace[]>
    return client.face.detectWithUrl(IMAGE_BASE_URL + imageName)
        .then(faces => {
            console.log(`${faces.length} face detected from image ${imageName} with ID ${faces[0].faceId}`)
            let id = faces[0].faceId
            return { imageName, id } 
        }).catch((err) => {
            console.log(`No face detected in: ${sourceImageFileNames[0]}.`)
            throw err;
        })
}));
console.log(values);

I'm also pretty certain that you didn't want to spread the imageName string into an object, and don't need to call Object.values on an array to get its values as an array.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • That totally worked, thanks! I would have never thought to return that whole thing. :D Now I just have to figure out how to convert the map results to an array, but that's another issue. Thanks again! I'll update the code (add it, not replace) to show the new printout. – Azurespot Oct 09 '19 at 19:11
  • @Azurespot The map results *are* an array. No conversion needed. – Bergi Oct 09 '19 at 19:12
  • thanks, urgh. After much trial and error, I'm seeing it's true. An array of 2 map objects, right? I've been trying to get the key `id` from the objects, but it's not letting me, saying `TypeError: sourceFaceMap[0].get is not a function` Is the object in the array not a map then? Really confused, because when I print it out, it has key value pairs. Here's my code: `console.log(sourceFaceMap) console.log(sourceFaceMap[0].get('id'))` – Azurespot Oct 09 '19 at 20:20
  • 1
    finally, finally I got it. It's a dictionary not a map. Boy I don't know JS well, lol. This gets the id I've been wanting: `console.log(sourceFaceMap[0]['id'])` Thank you again, you saved my job, literally. :) – Azurespot Oct 09 '19 at 20:27
  • 1
    Yes, the `{ imageName, id }` (short for `{"imageName": imageName, "id": id}`) that is returned is a plain object, not a map. If all you want to get are the ids, then you can replace the object with `return id;`. – Bergi Oct 09 '19 at 20:30
  • Thank you, much better! Although I still need it to be a pure array, but so much easier to deal without all those other keys. Here is the result after returning only the `id` and printing: `[ { id: 'f94360f5-feb3-4d14-816f-7d854fc0b34c' }, { id: '98091e1e-bc8d-4c93-a850-a115684a6e6e' } ]` – Azurespot Oct 09 '19 at 20:33
0

I think the problem you are facing here on line let id = response.faceId.

id is getting its value from response.faceId please do check that faceId property present in the response object. If it is present then code will work as you expected.

iAmADeveloper
  • 647
  • 3
  • 9
  • Thanks @iAmADeveloper, I was able to get values for my `id` variable. I edited the code a little, I was missing a resolve of the API promise, since this is what it returns, but then adding a Promise to the whole map() thing, maybe this is overkill? Just not sure how else to loop through an array and call the API on each array item (the source images). Does my new code look any more clear about the problem? Thanks. – Azurespot Oct 09 '19 at 18:50